Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁上。
package com.dason.second;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 多线程向固定大小缓冲区(队列)中存放数据以及获取数据
*
* @author Dason
*
*/
class BoundedBuffer {
final Lock lock = new ReentrantLock();
//使用两个condition 避免使用一个condition时极端唤醒
//如:若是一个condition,多个存放线程,当存满时,
//唤醒的是存放线程,则会进入死锁.
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
//缓冲区(队列)
final Object[] items = new Object[100];
// putptr:存数据索引;takeptr:取数据索引;count:队列中已有数量
int putptr, takeptr, count;
// 存数据
public void put(Object x) throws InterruptedException {
lock.lock();
try {
//当缓冲区中数据存满;存数据线程阻塞
while (count == items.length) {
notFull.await();
}
//开始存数据
items[putptr] = x;
//若已经存满,将putptr=0;重新从第一个开始存放
if (++putptr == items.length) {
putptr = 0;
}
//数量增1
++count;
//唤醒获取数据线程
notEmpty.signal();
} finally {
lock.unlock();
}
}
// 取数据
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length)
takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
package com.dason.second;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 3个线程一个线程打印一个字母,分别打印 A,B,C;
* 实现循环打印 A,B,C,A,B,C,A,B,C...
* @author Dason
*
*/
public class ConditionCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
business.sub1("B");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
business.sub2("C");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
business.main("A");
}
}
}).start();
}
static class Business {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
private int single = 0;
public void main(String str) {
lock.lock();
try {
while (single != 0) {
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(str);
single = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
public void sub1(String str) {
lock.lock();
try {
while (single != 1) {
try {
condition1.await();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(str);
single = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
public void sub2(String str) {
lock.lock();
try {
while (single != 2) {
try {
condition2.await();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(str);
single = 0;
condition.signal();
} finally {
lock.unlock();
}
}
}
}
1.使用synchronized出现的虚假唤醒
package com.dason.juc2;
/*
* 生产者和消费者案例
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer cus = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(cus, "消费者 B").start();
new Thread(pro, "生产者 C").start();
new Thread(cus, "消费者 D").start();
}
//店员
static class Clerk{
private int product = 0;
//进货
public synchronized void get(){
//存在虚假唤醒
if(product >= 1){
// while(product >= 1){//为了避免虚假唤醒问题,应该总是使用在循环中
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
this.notifyAll();
}
//卖货
public synchronized void sale(){
if(product <= 0){
// while(product <= 0){
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product);
this.notifyAll();
}
}
//生产者
static class Productor implements Runnable{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
clerk.get();
}
}
}
//消费者
static class Consumer implements Runnable{
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
clerk.sale();
}
}
}
}
2.使用condition避免虚假唤醒
package com.dason.juc2;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 生产者消费者案例:
*/
public class TestProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor pro = new Productor(clerk);
Consumer con = new Consumer(clerk);
new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start();
// new Thread(pro, "生产者 C").start();
// new Thread(con, "消费者 D").start();
}
}
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 进货
public void get() {
lock.lock();
try {
if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ ++product);
condition.signalAll();
} finally {
lock.unlock();
}
}
// 卖货
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : "
+ --product);
condition.signalAll();
} finally {
lock.unlock();
}
}
}
// 生产者
class Productor implements Runnable {
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
// 消费者
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
clerk.sale();
}
}
}