前言
前面已经了解了Synchronized
实现线程之间的通信,使用关键字synchronized
与wait()
方法和notify()
方式结合实现线程间通信,也就是等待/通知
模式。在ReentrantLock
中,是借助Condition
对象进行实现的。下面对Lock
实现线程之间的通信进行分析
Condition的创建方式如下:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition
按字面意思理解就是条件,当然,我们也可以将其认为是条件进行使用,这样的话我们可以通过上述的代码创建多个Condition
条件,我们就可以根据不同的条件
来控制现成的等待和通知。而我们还知道,在使用关键字synchronized
与wait()
方法和notify()
方式结合实现线程间通信的时候,notify/notifyAll
的通知等待的线程时是随机的,显然使用Condition相对灵活很多,可以实现”选择性通知
”。
这是因为,synchronized
关键字相当于整个Lock
对象只有一个单一
的Condition
对象,所有的线程都注册到这个对象上。线程开始notifAll
的时候,需要通知所有等待的线程,让他们开始竞争获得锁对象,没有选择权,这种方式相对于Condition
条件的方式在效率上肯定Condition
较高一些。
案例
实现多线程进行加一和减一的操作,当数字为1,进行减1,当数字为0,进行加1
主要方法对比如下:
(1)Object
的wait()
方法相当于Condition
类中的await()
方法;
(2)Object
的notify()
方法相当于Condition
类中的signal()
方法;
(3)Object
的notifyAll()
方法相当于Condition
类中的signalAll()
方法;
首先,使用Lock的时候都需要先获取锁。
示例代码如下:
package com.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author niuben
*/
//第一步,创建资源类
class LShare{
private int number = 0;
//创建Lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1
public void incr() throws InterruptedException {
//上锁
lock.lock();
try{
//判断
while(number != 0){
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其它线程
condition.signalAll();
}finally {
//解锁
lock.unlock();
}
}
//-1
public void decr() throws InterruptedException {
//上锁
lock.lock();
try{
//判断
while(number != 1){
condition.await();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其它线程
condition.signalAll();
}finally {
//解锁
lock.unlock();
}
}
}
public class LockDemo1 {
public static void main(String[] args) {
LShare share = new LShare();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"加加1").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"减减1").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"加加2").start();
new Thread(() -> {
for (int i = 0; i < 30; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"减减2").start();
}
}
运行效果
注意
为防止虚假唤醒,代码中的判断最好换成while
语句,
虚假唤醒可以参考一下,这篇博客https://blog.csdn.net/qq_39455116/article/details/87101633