多线程-基础-2[Concurrent包下的并发线程]

10.1、CountDownLatch :闭锁,线程递减锁

需要指定一个数字,可以同构await()方法产生阻塞,直到数字减为0时,阻塞自动被解开,可以通过contDown()方法使数字递减。经常用于监听某些初始化操作,等待初始化执行完毕后,通知主线程继续工作。

​
/**
 * ContDownLatch :用于程序资源初始化时
 */
@Slf4j
public class UserCountDownLatch {
    public static void main(String[] args) {
        final CountDownLatch countDownLatch = new CountDownLatch(2);
        /*主线程加载*/
        new Thread(()->{
          log.info("主线程{}加载资源,初始化开始...",Thread.currentThread().getName());
            try {
                countDownLatch.await();   //主线程阻塞
                log.info("主线程{}加载资源,初始化结束,开始执行任务...",Thread.currentThread().getName());
            } catch (InterruptedException e) {
                 log.error("主线程执行异常,原因:{}",e);
            }
        },"t1").start();
        /**
         * 模拟资源初始化加载1
         */
        new Thread(() -> {
            log.info("线程{}初始化数据开始......",Thread.currentThread().getName());
            try {
                Thread.sleep(5000);
                log.info("线程{}初始化数据结束......",Thread.currentThread().getName());
            } catch (InterruptedException e) {
                log.error("线程{}初始化数据异常......",Thread.currentThread().getName());
            }
            countDownLatch.countDown();
        },"t2").start();
        /**
         * 模拟资源初始化加载2
         */
        new Thread(() -> {
            log.info("线程{}初始化数据开始......",Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
                log.info("线程{}初始化数据结束......",Thread.currentThread().getName());
            } catch (InterruptedException e) {
                log.error("线程{}初始化数据异常......",Thread.currentThread().getName());
            }
            countDownLatch.countDown();
        },"t3").start();
    }
}
​
14:18:51.057 [t3] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 线程t3初始化数据开始......
14:18:51.057 [t1] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 主线程t1加载资源,初始化开始....
14:18:51.057 [t2] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 线程t2初始化数据开始......
14:18:54.061 [t3] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 线程t3初始化数据结束......
14:18:56.061 [t2] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 线程t2初始化数据结束......
14:18:56.061 [t1] INFO com.qiulin.study.thread.day03.UserCountDownLatch - 主线程t1加载资源,初始化结束,开始执行任务........

 

10.2、CyclicBarrier:栅栏

适用于希望多个线程在某一个节点找齐,当指定数量的线程都到达该节点时再回去同时放行接着执行。

/**
*eg:三个运动员进入赛道,当最后一个到达的时候,立即开始比赛
**/
@Slf4j
public class UseCyclicBarrier {
​
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        log.info("有3位选手陆续进入比赛场地.........");
        ExecutorService executorService = new ThreadPoolExecutor(3,3, 10,TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        Arrays.asList("张三","李四","王五").stream().forEach(x->{
            executorService.execute(new Runner(cyclicBarrier,x,  new Random().nextInt(10000)));
        });
        executorService.shutdown();
    }
​
    /**
     * Runner
     */
    static class Runner implements Runnable {
       private CyclicBarrier cyclicBarrier;
       private String name;
       private int takeTime;
​
       public Runner(CyclicBarrier cyclicBarrier,String name,int takeTime){
           this.cyclicBarrier = cyclicBarrier;
           this.name = name;
           this.takeTime = takeTime;
        }
        @Override
        public void run()   {
           try {
               log.info("运动员{}走向赛道...",name);
               Thread.sleep(takeTime);
               cyclicBarrier.await(); 
               log.info("运动员{}到达起点,开始比赛...",name);
               Thread.sleep(takeTime);
               log.info("运动员{}到达终点,比赛结束,用时{}秒...",name,takeTime);
           }catch (Exception e){
               log.error("运动员{}受伤,退出比赛...",name);
           }
        }
    }
}
14:22:53.809 [main] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 有3位选手陆续进入比赛场地.........
14:22:53.842 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员李四走向赛道...
14:22:53.842 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员张三走向赛道...
14:22:53.842 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员王五走向赛道...
14:23:02.507 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员张三到达起点,开始比赛...
14:23:02.507 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员王五到达起点,开始比赛...
14:23:02.507 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员李四到达起点,开始比赛...
14:23:04.920 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员张三到达终点,比赛结束,用时2413秒...
14:23:09.576 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员王五到达终点,比赛结束,用时7068秒...
14:23:11.171 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseCyclicBarrier - 运动员李四到达终点,比赛结束,用时8664秒...

10.3、Semaphore:信号量

Semaphonre是计数信号量。Semaphone管理一系列许可证。每个acquire方法阻塞,acquire()方法拿走一个许可证,每个release()释放一个许可。实际上并没有许可证这个对象,Semaphore只是维护了一个可获得许可证的数量。

**
 * Semaphore :信号量
 * eg:赛道上有4个赛道,每个赛道只允许一个人,有8个运动员需要比赛,当一个运动员跑完时,另一个运动员马上占用赛道
 */
@Slf4j
public class UseSemaphore {
    public static void main(String[] args) {
        //LinkedBlockingQueue:无界队列,所以maximunPoolSize大小无论设置多少,其实无效
        ExecutorService executorService = new ThreadPoolExecutor(4,4, 10, TimeUnit.SECONDS,new LinkedBlockingQueue<>());
        Semaphore semaphore = new Semaphore(4);
        LongStream.range(1,8).boxed().forEach(x->{
            executorService.execute(new Runner(semaphore,"线程"+x,new Random().nextInt(1000)));
        });
        executorService.shutdown();
    }
​
    /**
     * 运动员
     */
    static class Runner implements Runnable {
        private Semaphore semaphore;
        private String name;
        private int takeTime;
​
        public Runner(Semaphore semaphore,String name,int takeTime){
            this.semaphore = semaphore;
            this.name = name;
            this.takeTime = takeTime;
        }
        @Override
        public void run()   {
            try {
                log.info("运动员{}走向赛道...",name);
                Thread.sleep(takeTime);
                semaphore.acquire(); //获取许可
                log.info("运动员{}到达起点,开始比赛...",name);
                Thread.sleep(takeTime);
                semaphore.release(); //释放许可
                log.info("运动员{}到达终点,比赛结束,用时{}秒...",name,takeTime);
            }catch (Exception e){
                log.error("运动员{}受伤,退出比赛...",name);
            }
        }
    }
}
14:29:58.452 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程1走向赛道...
14:29:58.452 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程4走向赛道...
14:29:58.452 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程2走向赛道...
14:29:58.452 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程3走向赛道...
14:29:58.880 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程1到达起点,开始比赛...
14:29:59.258 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程4到达起点,开始比赛...
14:29:59.304 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程1到达终点,比赛结束,用时424秒...
14:29:59.304 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程5走向赛道...
14:29:59.406 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程3到达起点,开始比赛...
14:29:59.406 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程2到达起点,开始比赛...
14:29:59.882 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程5到达起点,开始比赛...
14:30:00.061 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程4到达终点,比赛结束,用时803秒...
14:30:00.061 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程6走向赛道...
14:30:00.235 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程6到达起点,开始比赛...
14:30:00.357 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程2到达终点,比赛结束,用时951秒...
14:30:00.357 [pool-1-thread-3] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程3到达终点,比赛结束,用时951秒...
14:30:00.357 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程7走向赛道...
14:30:00.409 [pool-1-thread-4] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程6到达终点,比赛结束,用时173秒...
14:30:00.461 [pool-1-thread-1] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程5到达终点,比赛结束,用时578秒...
14:30:00.846 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程7到达起点,开始比赛...
14:30:01.336 [pool-1-thread-2] INFO com.qiulin.study.thread.day03.UseSemaphore - 运动员线程7到达终点,比赛结束,用时489秒..

10.4、ReentrantLock:重入锁

在需要进行同步的代码部分加上锁定,最终一定要释放,否则会造成该锁永远无

法释放,其他线程永远进不来。

 

Lock接口实现类

 

 

Lock方法

10.4.1、线程同步

/**
 * 重入锁,线程阻塞
 */
@Slf4j
public class UseReentrantLock {
    private Lock lock = new ReentrantLock();
    public static void main(String[] args) {
       final UseReentrantLock reentrantLock = new UseReentrantLock();
        new Thread(() -> reentrantLock.test1()).start();
        new Thread(() -> reentrantLock.test2()).start();
    }
​
    public void test1(){
        try {
            lock.lock();
            log.info("当前线程:{}进入.....",Thread.currentThread().getName());
            Thread.sleep(2000);
            log.info("当前线程:{}退出.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
    public void test2(){
        try {
            lock.lock();
            log.info("当前线程:{}进入.....",Thread.currentThread().getName());
            Thread.sleep(2000);
            log.info("当前线程:{}退出.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
}
21:12:22.212 [Thread-0] INFO com.qiulin.study.thread.day03.UseReentrantLock - 当前线程:Thread-0进入.....
21:12:24.215 [Thread-0] INFO com.qiulin.study.thread.day03.UseReentrantLock - 当前线程:Thread-0退出.....
21:12:24.215 [Thread-1] INFO com.qiulin.study.thread.day03.UseReentrantLock - 当前线程:Thread-1进入.....
21:12:26.216 [Thread-1] INFO com.qiulin.study.thread.day03.UseReentrantLock - 当前线程:Thread-1退出.....

10.4.2、Condition :条件通知

使用Synchronized的时候,如果需要多个线程间进行通信需要Object的wait()和notify()、notifyAll()方法进行配合工作。

在Lock对象中,有一个新的等待通知类-Condition。这个Condition一定是针对具体的某一把锁的,在只有锁的基础上产生Condition。= 唤醒[不释放锁,直到finally中锁释放,才唤醒]【lock.newConditon()】

 
@Slf4j
public class UseCondition {
    private Lock lock = new ReentrantLock();
    /*获取condition实例*/
    private Condition condition = lock.newCondition();
    public static void main(String[] args) {
        final UseCondition useCondition = new UseCondition();
               new Thread(() ->{
            useCondition.method1();
        },"t1").start();
        new Thread(() ->{
            useCondition.method2();
        },"t2").start();
​
    }
​
    /**
     * 阻塞: condition.await();  // object wait()
     */
    public void method1(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(2000);
            log.info("当前线程{}任务执行完毕,阻塞.....",Thread.currentThread().getName());
            condition.await();  // object wait(),阻塞
            log.info("当前线程{}被唤醒.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
    /**
     * 唤醒[不释放锁,直到finally中锁释放,才唤醒]: condition.signal();  // object notify()
     */
    public void method2(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(3000);
            log.info("当前线程{}任务执行完毕,发出通知,唤醒阻塞线程......",Thread.currentThread().getName());
            condition.signal();  // object notify()  唤醒
            Thread.sleep(1000);
            log.info("线程{}继续执行.......",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
}
21:36:51.786 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1,开始执行任务....
21:36:53.788 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1任务执行完毕,阻塞.....
21:36:53.788 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t2,开始执行任务....
21:36:56.789 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t2任务执行完毕,发出通知,唤醒阻塞线程......
21:36:57.790 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 线程t2继续执行.......
21:36:57.790 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1被唤醒.....

10.4.3、多个Condition时,使用condition.signalAll()唤醒当前condition.await()的方法。

​
/**
 * Condition使用
 * 多个Condition的使用
 */
@Slf4j
public class UseCondition {
    private final Lock lock = new ReentrantLock();
    /*获取condition实例*/
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    public static void main(String[] args) {
        final UseCondition useCondition = new UseCondition();
               new Thread(() ->{ useCondition.method1(); },"t1").start();
               new Thread(() ->{ useCondition.method2(); },"t2").start();
               new Thread(() ->{ useCondition.method3(); },"t3").start();
               new Thread(() ->{ useCondition.method4(); },"t4").start();
               new Thread(() ->{ useCondition.method5(); },"t5").start();
    }
​
    /**
     * 阻塞: condition1.await();  // object wait()
     */
    public void method1(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(1000);
            log.info("当前线程{}任务执行完毕,阻塞.....",Thread.currentThread().getName());
            condition1.await();  // object wait(),阻塞
            log.info("当前线程{}被唤醒.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
    /**
     * 阻塞: condition1.await();  // object wait()
     */
    public void method2(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(2000);
            log.info("当前线程{}任务执行完毕,阻塞.....",Thread.currentThread().getName());
            condition1.await();  // object wait(),阻塞
            log.info("当前线程{}被唤醒.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
    /**
     * 阻塞: condition2.await();  // object wait()
     */
    public void method3(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(3000);
            log.info("当前线程{}任务执行完毕,阻塞.....",Thread.currentThread().getName());
            condition2.await();  // object wait(),阻塞
            log.info("当前线程{}被唤醒.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
    /*
     *唤醒[不释放锁,直到finally中锁释放,才唤醒]:condition1.signalAll(); 
     */
    public void method4(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(4000);
            log.info("当前线程{}任务执行完毕,阻塞.....",Thread.currentThread().getName());
            condition1.signalAll();  // object notifyAll()唤醒
            log.info("当前线程{}被唤醒.....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
​
​
​
    /**
     * 唤醒[不释放锁,直到finally中锁释放,才唤醒]: condition2.signal();  // object notify()
     */
    public void method5(){
        try {
            lock.lock();
            log.info("当前线程{},开始执行任务....",Thread.currentThread().getName());
            Thread.sleep(5000);
            log.info("当前线程{}任务执行完毕,发出通知,唤醒阻塞线程......",Thread.currentThread().getName());
            condition2.signal();  // object notify()  唤醒
            Thread.sleep(1000);
            log.info("线程{}继续执行.......",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("线程{},异常原因{}",Thread.currentThread().getName(),e);
        }finally {
            lock.unlock();
        }
    }
}
​
21:52:21.734 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1,开始执行任务....
21:52:22.738 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1任务执行完毕,阻塞.....
21:52:22.738 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t2,开始执行任务....
21:52:24.738 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t2任务执行完毕,阻塞.....
21:52:24.738 [t3] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t3,开始执行任务....
21:52:27.739 [t3] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t3任务执行完毕,阻塞.....
21:52:27.739 [t4] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t4,开始执行任务....
21:52:31.740 [t4] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t4任务执行完毕,阻塞.....
21:52:31.740 [t4] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t4被唤醒.....
21:52:31.740 [t5] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t5,开始执行任务....
21:52:36.741 [t5] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t5任务执行完毕,发出通知,唤醒阻塞线程......
21:52:37.741 [t5] INFO com.qiulin.study.thread.day03.UseCondition - 线程t5继续执行.......
21:52:37.741 [t1] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t1被唤醒.....
21:52:37.741 [t2] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t2被唤醒.....
21:52:37.741 [t3] INFO com.qiulin.study.thread.day03.UseCondition - 当前线程t3被唤醒.....

 

10.4.4、ReentrantReadWriteLock:读写锁

读写锁ReentrantReadWriteLock,其核心就是实现读写分离的锁。在高并发场景下,尤其是在读多写少的情况下,性能远高于重入锁。但是在写多读少的情况下,使用ReentrantLock性能高于读写锁。

对于Synchronized、ReentrantLock锁而言,同一时间内,只能有一个线程访问被锁定的代码块。而读写锁本质是读写分离。即包含两个锁(读锁,写锁)。在读锁情境下,多个线程可以并发访问,但是在写锁的时候,只能一个一个顺序访问。即【读读共享,写写互斥,读写互斥】


/**
 * 读写锁(读多写少的场景下使用)
 */
@Slf4j
public class UseReentrantLockReadAndWrite {
    private final ReentrantReadWriteLock lock= new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
​
    public static void main(String[] args) {
        UseReentrantLockReadAndWrite uselock = new UseReentrantLockReadAndWrite();
        new Thread(() ->{uselock.read1();},"t1").start();
        new Thread(() ->{uselock.read1();},"t2").start();
        new Thread(() ->{uselock.write1();},"t3").start();
        new Thread(() ->{uselock.write2();},"t4").start();
    }
​
​
    /**
     * 读 //4s
     */
    private void read1(){
        try {
            readLock.lock();
            log.info("当前线程:{}读取数据开始....",Thread.currentThread().getName());
            Thread.sleep(4000);
            log.info("当前线程:{}读取数据结束....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        } finally {
            readLock.unlock();
        }
    }
​
    /**
     * 读  //3s
     */
    private void read2(){
        try {
            readLock.lock();
            log.info("当前线程:{}读取数据开始....",Thread.currentThread().getName());
            Thread.sleep(3000);
            log.info("当前线程:{}读取数据结束....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        } finally {
            readLock.unlock();
        }
    }
​
    /**
     * 写 //2s
     */
    private void write1(){
        try {
            writeLock.lock();
            log.info("当前线程:{}写入数据开始....",Thread.currentThread().getName());
            Thread.sleep(2000);
            log.info("当前线程:{}写入数据结束....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        } finally {
            writeLock.unlock();
        }
    }
​
    /**
     * 写 //3s
     */
    private void write2(){
        try {
            writeLock.lock();
            log.info("当前线程:{}写入数据开始....",Thread.currentThread().getName());
            Thread.sleep(3000);
            log.info("当前线程:{}写入数据结束....",Thread.currentThread().getName());
        }catch (Exception e){
            log.error("当前线程:{},出现异常,原因:{}",Thread.currentThread().getName(),e);
        } finally {
            writeLock.unlock();
        }
    }
}
22:33:22.507 [t2] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t2读取数据开始....
22:33:22.507 [t1] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t1读取数据开始....
22:33:26.511 [t2] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t2读取数据结束....
22:33:26.511 [t1] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t1读取数据结束....
22:33:26.511 [t3] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t3写入数据开始....
22:33:28.512 [t3] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t3写入数据结束....
22:33:28.512 [t4] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t4写入数据开始....
22:33:31.513 [t4] INFO com.qiulin.study.thread.day03.UseReentrantLockReadAndWrite - 当前线程:t4写入数据结束....

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章