Java中juc并发包下的Condition接口与ReentrantLock对象锁实现线程通信

前言

在传统的Java开发中,大多数程序员都是使用synchronized关键字配合Object类中的wait()、notify()方法和notifyAll()方法来实现线程通信,不过随着jdk版本的不断升级与维护,在jdk1.5开始,JavaAPI中出现了一个叫ReentrantLock对象锁,它是一种可重入的、递归的非公平锁,下面我们就来简单介绍一下ReentrantLock锁。

 

ReentrantLock继承结构体系图

synchronized关键字与ReentrantLock重入锁的对比

Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的。与synchronized相比,重入锁有着显式的操作过程,开发人员必须手动的指定何时加锁(调用lock方法),何时释放锁(调用unlock方法)。因此,重入锁对逻辑的控制性要好于synchronized。重入锁,顾名思义,对于同一个线程,这种锁是可以反复进入的。如果一个线程多次获得锁,那么在释放锁的时候,也必须释放相同的次数。

构造器

 常用方法

 1 lock()
 2 
 3 获得锁,如果锁已经被占用,则等待
 4 
 5 lockInterruptibly()
 6 
 7 获得锁,但优先响应中断。如果当前线程未被中断,则获取锁
 8 
 9 tryLock()
10 
11 尝试获得锁,如果成功放回true,失败返回false,该方法不等待,立即返回
12 
13 unlock()
14 
15 释放锁
16 
17 newCondition()
18 
19 返回与lock实例一起使用的Condition对象

案例演示

需求:

  现有三个线程A、B、C,要求这三个线程以A -> B -> C 这种顺序来轮流干活(10个轮回)

具体实现:

通过实现Condition对象来实现多线程之间通信

通过一个变量num来标识当前该哪个线程干活

规定:

num = 1时,线程A干活

num = 2时,线程B干活

num = 3时,线程C干活

测试代码:

  1 package com.lzp.lock.condition;
  2 
  3 import java.util.concurrent.locks.Condition;
  4 import java.util.concurrent.locks.Lock;
  5 import java.util.concurrent.locks.ReentrantLock;
  6 
  7 /**
  8  * @Author LZP
  9  * @Date 2021/7/1 20:41
 10  * @Version 1.0
 11  *
 12  * 需求:
 13  *  现有三个线程A、B、C,要求这三个线程以A -> B -> C 这种顺序来轮流干活(10个轮回)
 14  * 通过实现Condition对象来实现多线程之间的通信
 15  * 通过一个变量num来标识当前该哪个线程干活
 16  * 规定:
 17  * num = 1时,线程A干活
 18  * num = 2时,线程B干活
 19  * num = 3时,线程C干活
 20  */
 21 public class ConditionDemo01 {
 22 
 23     private int num = 1;
 24 
 25     private final Lock lock = new ReentrantLock();
 26     private final Condition cA = lock.newCondition();
 27     private final Condition cB = lock.newCondition();
 28     private final Condition cC = lock.newCondition();
 29 
 30     public void doA(int i) {
 31         // 上锁
 32         lock.lock();
 33         try {
 34             while (num != 1) {
 35                 // 等待
 36                 try {
 37                     cA.await();
 38                 } catch (InterruptedException e) {
 39                     e.printStackTrace();
 40                 }
 41             }
 42             // 干活
 43             System.out.println(Thread.currentThread().getName() + "\t" + i);
 44             // 修改标志位
 45             num = 2;
 46             // 通知线程B
 47             cB.signal();
 48         } finally {
 49             lock.unlock();
 50         }
 51     }
 52 
 53     public void doB(int i) {
 54         // 上锁
 55         lock.lock();
 56         try {
 57             while (num != 2) {
 58                 // 等待
 59                 try {
 60                     cB.await();
 61                 } catch (InterruptedException e) {
 62                     e.printStackTrace();
 63                 }
 64             }
 65             // 干活
 66             System.out.println(Thread.currentThread().getName() + "\t" + i);
 67             // 修改标志位
 68             num = 3;
 69             // 通知线程C
 70             cC.signal();
 71         } finally {
 72             lock.unlock();
 73         }
 74     }
 75 
 76     public void doC(int i) {
 77         // 上锁
 78         lock.lock();
 79         try {
 80             while (num != 3) {
 81                 // 等待
 82                 try {
 83                     cC.await();
 84                 } catch (InterruptedException e) {
 85                     e.printStackTrace();
 86                 }
 87             }
 88             // 干活
 89             System.out.println(Thread.currentThread().getName() + "\t" + i);
 90             // 修改标志位
 91             num = 1;
 92             // 通知线程A
 93             cA.signal();
 94         } finally {
 95             lock.unlock();
 96         }
 97     }
 98 
 99     public static void main(String[] args) {
100         ConditionDemo01 source = new ConditionDemo01();
101         new Thread(() -> {
102             for (int i = 0; i < 10; i++) {
103                 source.doA(i);
104             }
105         }, "A").start();
106         new Thread(() -> {
107             for (int i = 0; i < 10; i++) {
108                 source.doB(i);
109             }
110         }, "B").start();
111         new Thread(() -> {
112             for (int i = 0; i < 10; i++) {
113                 source.doC(i);
114             }
115         }, "C").start();
116     }
117 }

运行结果:

ReentrantLock的特点

1. ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。

2. ReentrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

3. ReentrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。应用场景:需要使用以上三个特点时,使用ReentrantLock。

应用场景:需要使用以上三个特点时,使用ReentrantLock

Condition的使用

1. 使用ReentrantLock类的newCondition()方法可以获取Condition对象

2. 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法

3. 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

posted @ 2021-07-01 21:32  没有你哪有我  阅读(82)  评论(0编辑  收藏  举报