Java高并发编程中Condition在Lock中所充当的角色及用法-刘宇
作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。
一、什么是Condition?
condition对象是依赖于lock对象的,是通过lock对象调用newCondition()方法创建出来的,虽然我们可以通过new来创建Condition但是那样也没有任何意义,因为他没有和锁挂钩。
- 作用
在使用Lock锁之前,我们通常都是使用synchronized关键字来进行同步。还有就是Object中的wait、notify等方法来实现等待/通知。那么现在我们使用Lock锁时,利用Lock来进行同步,而Condition可以帮我们实现等待和通知。
二、Condition常用方法
1、await方法
//使当前线程处于等待状态,除非被唤醒或被中断
void await() throws InterruptedException;
//当前线程等待特定时间后自动唤醒,也可以被提前唤醒或打断
boolean await(long time, TimeUnit unit) throws InterruptedException
2、awaitNanos方法
//使用纳秒,使当前线程等待特定时间后自动唤醒,也可以被提前唤醒或打断。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
long awaitNanos(long nanosTimeout) throws InterruptedException;
3、awaitUninterruptibly方法
//使当前线程处于等待状态,除非被唤醒,但是不可被中断
void awaitUninterruptibly();
4、awaitUntil方法
使当前线程处于等待状态,除非被唤醒、被打断、到达指定日期时。如果没有到指定时间就被唤醒,则返回true,否则表示到了指定时间,返回返回false。
boolean awaitUntil(Date deadline) throws InterruptedException;
5、signal方法
//唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
void signal();
6、signalAll方法
//唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
void signalAll();
二、Condition练习
1、单个Condition制作生产者与消费者
package com.brycen.part3.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
public class ConditionExample {
final static ReentrantLock lock = new ReentrantLock();
final static Condition condition = lock.newCondition();
static int data = 0;
static boolean noUse = false;
public static void main(String[] args) {
//开启两个生产者
Stream.of("P1","P2").forEach(i->new Thread(new Runnable() {
@Override
public void run() {
while (true){
produce();
}
}
}, i).start());
//开启两个消费者
Stream.of("C1","C2").forEach(i->new Thread(new Runnable() {
@Override
public void run() {
while (true){
consume();
}
}
}, i).start());
}
public static void produce(){
try {
lock.lock();
while (noUse){
condition.await();
}
data++;
//休眠1秒,模拟生产数据耗时
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+" -> "+data);
noUse = true;
//唤醒所有线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void consume(){
try {
lock.lock();
while (!noUse){
//进行阻塞
condition.await();
}
System.out.println(Thread.currentThread().getName()+" -> "+data);
noUse = false;
//唤醒所有线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
运行结果:
P1 -> 1
C2 -> 1
P2 -> 2
C1 -> 2
P1 -> 3
C2 -> 3
P2 -> 4
C1 -> 4
P1 -> 5
C2 -> 5
P2 -> 6
C1 -> 6
...
2、多个Condition制作生产者与消费者
- 如果使用单个Condition编写如下代码则会存在当生产者生产完后调用唤醒方法可能唤醒的是另一个生产者的问题,那么我们使用两个Condition将生产者与消费者区分即可。
package com.brycen.concurrency03.lock;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class ConditionExample2 {
final static ReentrantLock LOCK = new ReentrantLock();
final static Condition PRODUCE_COND = LOCK.newCondition();
final static Condition CONSUME_COND = LOCK.newCondition();
final static LinkedList<Long> LIST = new LinkedList<>();
final static int MAX_CAPACITY = 100;
public static void main(String[] args) {
IntStream.range(1,6).forEach(i->{
new Thread(new Runnable() {
@Override
public void run() {
while (true){
produce();
//休眠500毫秒
sleep(500);
}
}
},"P"+i).start();
});
IntStream.range(1,8).forEach(i->{
new Thread(new Runnable() {
@Override
public void run() {
while (true){
consume();
//休眠500毫秒
sleep(500);
}
}
},"C"+i).start();
});
}
public static void sleep(long milliSeconds) {
try {
TimeUnit.MILLISECONDS.sleep(milliSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void produce(){
try {
LOCK.lock();
while (LIST.size()>=MAX_CAPACITY){
PRODUCE_COND.await();
}
long value = System.currentTimeMillis();
//将值加入到尾部中去
LIST.addLast(value);
System.out.println(Thread.currentThread().getName()+" -> "+value);
//唤醒消费Condition上的所有线程
CONSUME_COND.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
public static void consume(){
try {
LOCK.lock();
while (LIST.isEmpty()){
CONSUME_COND.await();
}
//将列表中第一个元素删除
Long value = LIST.removeFirst();
System.out.println(Thread.currentThread().getName()+" -> "+value);
//唤醒生产Condition上的所有线程
PRODUCE_COND.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
LOCK.unlock();
}
}
}
运行结果:
P1 -> 1596642680298
P4 -> 1596642680299
P2 -> 1596642680300
P3 -> 1596642680300
P5 -> 1596642680300
C1 -> 1596642680298
C2 -> 1596642680299
C3 -> 1596642680300
C4 -> 1596642680300
C5 -> 1596642680300
P5 -> 1596642680805
P4 -> 1596642680805
P3 -> 1596642680805
P1 -> 1596642680806
P2 -> 1596642680806
C6 -> 1596642680805
C7 -> 1596642680805
C3 -> 1596642680805
C2 -> 1596642680806
...