多線程-基礎-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寫入數據結束....

 

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