Condition的功能类似于Object.wait()和Object.notify()功能,来实现线程间的协调与通信。
之前的步互斥与通信(练习)例子用condition实现:
package ThreadPractice;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by qiaorenjie on 2018/3/30.
*/
public class ConditionCommunication {
/*
问:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,
接着再回到主线程又循环100次,如此循环50次。
*/
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i); // 这个方法结束business会释放,所以要加final,而主线程则可不加
}
}
}).start();
// main 方法占用的主线程直接用main的线程
for (int i = 1; i <= 50 ; i++) {
business.main(i);
}
}
/*
放在本类ConditionCommunication外就相当于Business.java与外面的重复了会报错,
而拉到类的内部就是内部内就是ConditionCommunication.Buiness.java,静态方法调用时候再加静态
*/
static class Business {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition(); // condition基于lock上
private boolean flag = true;
public void sub(int i) {
lock.lock();
try {
while (!flag){
try {
condition.await(); // 自己的 await方法不要写错obj的 wait
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("子线程循环:" + j + " 循环了:" + i);
}
flag = !flag;
condition.signal(); // signal代替notify
}finally {
lock.unlock();
}
}
public synchronized void main(int i) {
lock.lock();
try {
while (flag){
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("主线程循环:" + j + " 循环了:" + i);
}
flag = !flag;
condition.signal();
}finally {
lock.unlock();
}
}
}
}
Condition与wait与notify想比能实现其不能实现的功能,比如:阻塞队列、三个或多个线程唤醒指定线程等
Obj下的notify()用于唤醒其中一个线程,而notifyAll()则是唤醒全部线程,所以想完成唤醒指定线程不太方便,而利用condition则非常容易。
condition可以理解成一个线程间协调通信的工具类,让线程在某个条件下(condition)等待await(),让对应线程的条件被传递信号signal时说明线程又可以重新唤醒恢复。
比如在之前做的子线程与主线程的练习下又有新的需求:
(新增需求) : 在1号线程完成循环后唤醒2号,2号完成后唤醒3号,3号再到1号循环。
public class ThreeConditionCommunication {
/*
问:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次。在1号线程完成循环后唤醒2号,2号完成后唤醒3号,3号再到1号循环。
*/
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i); // 这个方法结束business会释放,所以要加fina,而主线程则可不加
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub3(i); // 这个方法结束business会释放,所以要加fina,而主线程则可不加
}
}
}).start();
// main 方法占用的主线程直接用main的线程
for (int i = 1; i <= 50 ; i++) {
business.main(i);
}
}
/*
放在本类ConditionCommunication外就相当于Business.java与外面的重复了会报错,
而拉到类的内部就是内部内就是ConditionCommunication.Buiness.java,静态方法调用时候再加静态
*/
static class Business {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int turn = 1;
public void sub2(int i) {
lock.lock();
try {
while (turn != 2){ // !=2说明不该线程2
try {
condition2.await(); //不该2那么2就等着
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("线程2号循环:" + j + " 循环了:" + i);
}
turn = 3;
condition3.signal(); // 交给第三个线程
}finally {
lock.unlock();
}
}
public void sub3(int i) {
lock.lock();
try {
while (turn != 3){
try {
condition3.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 20; j++) {
System.out.println("线程3号循环:" + j + " 循环了:" + i);
}
turn = 1;
condition1.signal();
}finally {
lock.unlock();
}
}
public synchronized void main(int i) {
lock.lock();
try {
while (turn != 1){
try {
condition1.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("线程1号循环:" + j + " 循环了:" + i);
}
turn = 2;
condition2.signal();
}finally {
lock.unlock();
}
}
}
}
通过对不同线程对应的condition进行signal达到唤醒指定线程的操作。
那么Condition是怎么实现的呢?
image.png
由代码点击进入发现lock.newCondition() 返回的是Lock接口下的Condition接口的实现,查询Condition发现该实现被一个叫 AbstractQueuedSynchronizer (AQS) 同步队列中被实现,如下:
image.png
类ConditionObject实现了Condition接口,而这个类作为了AbstractQueuedSynchronizer的内部类
image.png
这里又是利用到了AQS的原理,AQS在多线程中有着很重要的地位。