美文网首页
JAVA并发相关知识

JAVA并发相关知识

作者: 欧阳的博客 | 来源:发表于2019-11-04 15:09 被阅读0次

看了java核心技术 卷1的第14章《并发》,在此做点笔记,供以后复习。

线程睡眠函数(java.lang.Thread)

static void sleep(long millis)

实现多线程的两种方式:

第一种是继承Thread类,复写run方法。然后实例化这个类,调用实例的start()方法就能运行了。这样不好的地方在于没有把运行任务和运行机制解耦合。如果有很多任务的话,需要为每个任务都新建一个独立的线程,付出的代价太大。并且Thread类也是实现了Runnable接口。


class aa extends Thread {
  public void run() {
      System.out.println(Thread.currentThread() );
    }
}

public class TestThread {
    public static void main(String args[]) {
        for(int i=0;i<10;i++) {
            Thread ww =new aa();
            ww.start();
        }
    }
}

第二种是实现Runnable接口。aa是用来写任务的,然后新建线程去执行任务。这样实现了运行任务和运行机制的解耦。


class aa implements Runnable {

    public void run() {
        System.out.println(Thread.currentThread() );
    }

}

public class TestThread {

    public static void main(String args[]) {
        for(int i=0;i<10;i++) {
            Thread ww = new Thread(new aa());
            ww.start();
        }
    }
}

任务的声明还可以采用lambda表达式

   Runnable r = ()->{
            System.out.println(Thread.currentThread());
        };    

中断线程

当线程的run方法执行方法体最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。没有可以强制终止线程的方法。

一般会用interrupt方法来请求终止线程。interrupt方法本身是中断请求,但是代码里面可以自己写逻辑来根据中断结束线程。但是当线程处于阻塞状态下去请求中断,线程会抛出中断异常。
如下代码:

//将业务代码写在do more work中,一旦接收到了中断就return了
//否则一直检测中断标志位是否被置位。
while(!Thread.currentThread().isInterrupted()){
do more work
}

中断的一些函数(java.lang.Thread)

  • void interrupt()
    向线程发送中断请求。
  • static boolean intrrupted()
    测试当前线程是否被中断。调用后当前线程的中断状态会重置为false。
  • boolean isInterrupted()
    测试线程是否被终止。不改变线程的中断状态
  • static Thread currentThread()
    返回代表当前执行线程的Thread对象。

线程状态

  • New(新创建创建)
  • Runnable(可运行)
  • Blocked(被阻塞)
  • Wating(等待)
  • Timed waiting(计时等待)
  • Terminated(被终止)
1、新创建线程

当new一个新线程的时候,线程还没有开始运行的状态。

2、可运行线程

一旦调用start方法,线程处于runnable状态。一个可运行线程可能正在运行,也可能没有运行。只是说该线程可以运行。

3、被阻塞线程和等待线程
  • 当一个线程视图获取一个内部的对象锁时,而该锁被其他线程持有,则该线程进入阻塞状态。
  • 当线程等待另一个线程通知调度器一个条件时,它自己进入等待状态。
  • 有几个方法有一个超时参数。调用他们会导致线程进入计时等待状态。如Thread.sleep、Object.wait、Thread.join等
4、被终止的线程
  • 因为run方法正常退出而自然死亡。
  • 因为一个没有捕获的异常终止了run方法而意外死亡。

线程属性

1、线程优先级

每一个线程都有优先级,继承它父线程的优先级。可以使用setPriority方法来提高或者降低任何一个线程的优先级。但是设置了也不一定按设置的来,还是取决于操作系统的安排。

2、守护线程

为其他线程提供服务的线程。没有其他线程,守护线程就没有意义。注 :不要用守护线程访问固定资源。虽然目前不知道含义,但是注意一下。

3、未捕获异常处理器

如果没有对应的异常处理,那所有的异常都会默认传递到一个未捕获异常处理器。

void uncaughtException(Thread t,Throwable e)

同步

当不同的线程访问相同的资源时,会发生竞争的情况,这样会造成数据错误。有两种机制放置代码块受到并发访问的干扰。

lock、unlock和synchronized关键字

lock是上锁,unlock是解锁,unlock一般放在finally中,防止在临界区代码结束之前抛出了异常,而线程还依旧一直处于锁定状态。
条件对象的意思是当线程进行到某一步时,发现没满足条件,此时不能往下进行,所以不需要继续占用锁了。此时把该线程挂起来,并放弃锁。然后等待其他线程来激活这个被挂起来的锁。挂起来的函数为Condition.await(),激活函数为Condition.signalAll()

//没有条件对象的情况
mylock.lock();//上锁
try
{
critical section
}
finally
{
myLock.unlock();//解锁
}


//有条件对象的情况
private Condition condition;
mylock.lock();//上锁
try
{
    while(!(ok to proceed))
        condition.await();//等待激活
        do something
        condition.signalAll();//激活
}
finally
{
myLock.unlock();//解锁
}

Synchronized关键字声明类或者方法即可以上锁。然后直接调用wait()和notifyAll()就可以让线程进入等待和解除等待。

以下是我现在用的redis共享锁的代码###############
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁的简单实现代码
 */
@Service
public class DistributedLock {

    @Autowired
    private StringRedisTemplate stringRedisTemplate; 

    /**
     * Redis SetNX 指令,stringRedisTemplate是封装了redis基础操作,所以不含有setnx指令。这里就通过它本身的接口来实现setnx。
     */
    private Boolean setnx(String key, Object value) {
        return stringRedisTemplate.execute((RedisConnection conn) -> {
            try {
                return conn.setNX(stringRedisTemplate.getStringSerializer().serialize(key),
                        stringRedisTemplate.getStringSerializer().serialize(value.toString()));
            } finally {
                conn.close();
            }
        });

    }

    /**
     * 加锁
     *
     * @param lockName       锁的key
     * @param acquireTimeout 获取超时时间
     * @param timeout        锁的超时时间
     * @return 锁标识
     */
    public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {


        String retIdentifier = "0";
        try {
            // 随机生成一个value
            //String identifier = UUID.randomUUID().toString();
            // System.out.println("identity:"+identifier);
            // 锁名,即key值
            String lockKey = "lock:" + lockName;
            // 超时时间,上锁后超过此时间则自动释放锁
            int lockExpire = (int) (timeout / 1000);

            // 获取锁的超时时间,超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                if (this.setnx(lockKey, "1")) {
                    stringRedisTemplate.opsForValue().set(lockKey, "1", lockExpire, TimeUnit.SECONDS);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = "1";
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (stringRedisTemplate.getExpire(lockKey) == -1) {
                    stringRedisTemplate.opsForValue().set(lockKey, "1", lockExpire, TimeUnit.SECONDS);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     *
     * @param lockName   锁的key
     * @param identifier 释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName) {

        String lockKey = "lock:" + lockName;
        boolean retFlag = false;

        try {
            // 通过key值判断是不是该锁,若是该锁,则删除,释放锁
            stringRedisTemplate.delete(lockKey);
            retFlag = true;
        } catch (Exception e) {

        } finally {

        }
        return retFlag;
    }
}

该共享锁只是实现了基本的分布式锁,能够满足不同服务器争夺同一资源的场景。获取锁超时时间和自动释放锁的时间需要自己衡量。该方法没有实现可重入锁,原理也不难,就是存储的锁key的value是一个随机数就可以代表用户这次获取锁的身份,就可以重入了。具体的细节没做,就没发言权了。因为业务到这一步就够用了。

相关文章

网友评论

      本文标题:JAVA并发相关知识

      本文链接:https://www.haomeiwen.com/subject/uhxtuctx.html