Condition(条件),通常用于与 Lock(如:上篇学习过的 ReentrantLock)结合使用,用于线程间的通信,让线程阻塞(await())和唤醒(signal()),作用类似于Object 的 wait(),notify() .
Condition 与 Lock 绑定,一个 Lock 可以创建无数个 Condition。
以下用一个“吃回旋寿司”的例子介绍一下(DEMO1):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | public class Test { /** * 回旋寿司机器 */ static class SushiMachine { // 非公平 Lock mLock = new ReentrantLock(); Condition mEmptyCondition = mLock.newCondition(); Condition mFullCondition = mLock.newCondition(); // 单位:碟 final int MAX_DISH = 50; final int MIX_DIASH = 0; int mCurrentDish = 10; // 制作寿司 public void produce(int dish) { mLock.lock(); try { while (mCurrentDish >= MAX_DISH) { mFullCondition.await(); } if (mCurrentDish + dish - MAX_DISH > 0) { int offset = MAX_DISH - mCurrentDish; mCurrentDish += offset; System.out.println(" 制作寿司 --- 想放 " + dish + " 碟,放不下这么多,只能放 " + offset + " 碟,放了后还剩 " + mCurrentDish + " 碟 "); } else { mCurrentDish += dish; System.out.println(" 制作寿司 --- 想放 " + dish + " 碟,放了 " + dish + " 碟,放了后还剩 " + mCurrentDish + " 碟 "); } mEmptyCondition.signal(); } catch (InterruptedException e) { } finally { mLock.unlock(); } } // 吃寿司 public void consume(int dish) { mLock.lock(); try { while (mCurrentDish <= MIX_DIASH) { mEmptyCondition.await(); } if (mCurrentDish - dish < MIX_DIASH) { int mark = mCurrentDish; mCurrentDish = MIX_DIASH; System.out.println(" 吃寿司 --- 客人吃了 " + mark + " 碟,没得再吃了,现在还剩 " + mCurrentDish + " 碟 "); } else { mCurrentDish = mCurrentDish - dish; System.out.println(" 吃寿司 --- 客人吃了 " + dish + " 碟,现在还剩 " + mCurrentDish + " 碟 "); } mFullCondition.signal(); } catch (InterruptedException e) { } finally { mLock.unlock(); } } } /** * 小白兔回旋寿司店 */ static class SushiStore { SushiMachine mSushiMachine = new SushiMachine(); void produce(final int dish) { new Thread(new Runnable() { @Override public void run() { mSushiMachine.produce(dish); } }).start(); } void consume(final int dish) { new Thread(new Runnable() { @Override public void run() { mSushiMachine.consume(dish); } }).start(); } } public static void main(String[] var0) { // 测试 Test.SushiStore sushiStore = new Test.SushiStore(); sushiStore.consume(2); sushiStore.produce(30); sushiStore.consume(40); sushiStore.consume(5); sushiStore.produce(10); sushiStore.produce(50); sushiStore.consume(30); } } |
DEMO1 的某次运行结果输出:
1 2 3 4 5 6 7 | 吃寿司 --- 客人吃了 2 碟,现在还剩 8 碟 制作寿司 --- 想放 30 碟,放了 30 碟,放了后还剩 38 碟 吃寿司 --- 客人吃了 38 碟,没得再吃了,现在还剩 0 碟 制作寿司 --- 想放 10 碟,放了 10 碟,放了后还剩 10 碟 制作寿司 --- 想放 50 碟,放不下这么多,只能放 40 碟,放了后还剩 50 碟 吃寿司 --- 客人吃了 5 碟,现在还剩 45 碟 吃寿司 --- 客人吃了 30 碟,现在还剩 15 碟 |
若使用公平锁(DEMO2):
1 2 | // 公平锁 Lock mLock = new ReentrantLock(true); |
DEMO2 的某次运行结果输出:
1 2 3 4 5 6 7 | 吃寿司 --- 客人吃了 2 碟,现在还剩 8 碟 制作寿司 --- 想放 30 碟,放了 30 碟,放了后还剩 38 碟 吃寿司 --- 客人吃了 38 碟,没得再吃了,现在还剩 0 碟 制作寿司 --- 想放 10 碟,放了 10 碟,放了后还剩 10 碟 吃寿司 --- 客人吃了 5 碟,现在还剩 5 碟 制作寿司 --- 想放 50 碟,放不下这么多,只能放 45 碟,放了后还剩 50 碟 吃寿司 --- 客人吃了 30 碟,现在还剩 20 碟 |
以上 demo 可以使用我们用得比较多的 Object 的 wait 和 notify 吗?
答案是:不可以。
DEMO3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | public class Test { /** * 回旋寿司机器 */ static class SushiMachine { Lock mLock = new ReentrantLock(true); Object mEmptyCondition = new Object(); Object mFullCondition = new Object(); // 单位:碟 final int MAX_DISH = 50; final int MIX_DIASH = 0; int mCurrentDish = 10; // 制作寿司 public void produce(int dish) { mLock.lock(); try { while (mCurrentDish >= MAX_DISH) { synchronized (mFullCondition){ mFullCondition.wait(); } } if (mCurrentDish + dish - MAX_DISH > 0) { int offset = MAX_DISH - mCurrentDish; mCurrentDish += offset; System.out.println(" 制作寿司 --- 想放 " + dish + " 碟,放不下这么多,只能放 " + offset + " 碟,放了后还剩 " + mCurrentDish + " 碟 "); } else { mCurrentDish += dish; System.out.println(" 制作寿司 --- 想放 " + dish + " 碟,放了 " + dish + " 碟,放了后还剩 " + mCurrentDish + " 碟 "); } synchronized (mEmptyCondition){ mEmptyCondition.notify(); } } catch (InterruptedException e) { } finally { mLock.unlock(); } } // 吃寿司 public void consume(int dish) { mLock.lock(); try { while (mCurrentDish <= MIX_DIASH) { synchronized (mEmptyCondition){ mEmptyCondition.wait(); } } if (mCurrentDish - dish < MIX_DIASH) { int mark = mCurrentDish; mCurrentDish = MIX_DIASH; System.out.println(" 吃寿司 --- 客人吃了 " + mark + " 碟,没得再吃了,现在还剩 " + mCurrentDish + " 碟 "); } else { mCurrentDish = mCurrentDish - dish; System.out.println(" 吃寿司 --- 客人吃了 " + dish + " 碟,现在还剩 " + mCurrentDish + " 碟 "); } synchronized (mFullCondition){ mFullCondition.notify(); } } catch (InterruptedException e) { } finally { mLock.unlock(); } } } /** * 小白兔回旋寿司店 */ static class SushiStore { SushiMachine mSushiMachine = new SushiMachine(); void produce(final int dish) { new Thread(new Runnable() { @Override public void run() { mSushiMachine.produce(dish); } }).start(); } void consume(final int dish) { new Thread(new Runnable() { @Override public void run() { mSushiMachine.consume(dish); } }).start(); } } public static void main(String[] var0) { // 测试 Test.SushiStore sushiStore = new Test.SushiStore(); sushiStore.consume(2); sushiStore.produce(30); sushiStore.consume(40); sushiStore.consume(5); sushiStore.produce(10); sushiStore.produce(50); sushiStore.consume(30); } } |
DEMO3 的某次运行结果输出:
1 2 3 | 吃寿司 --- 客人吃了 2 碟,现在还剩 8 碟 制作寿司 --- 想放 30 碟,放了 30 碟,放了后还剩 38 碟 吃寿司 --- 客人吃了 38 碟,没得再吃了,现在还剩 0 碟 |
程序并没有运行完,而是进入了死锁。
分析:
DEMO3 使用的是公平锁,所以会按顺序获取锁执行,输出了 3 句,也就是执行了前三个任务,第四个任务就死锁了。
而第三个任务的时候就已经把寿司吃完,所以执行第四个任务的时候会进入 mEmptyCondition.wait(); 阻塞,而阻塞的线程已经获取到了互斥锁( mLock.lock(); ),mEmptyCondition.wait() 只释放它自己的同步锁,并没有释放线程的互斥锁,所以制作寿司线程的 produce 方法也就无法执行,无法制作寿司,也没寿司可吃,最终进入死锁。
而 DEMO1 的 mEmptyCondition.await() 会释放互斥锁,让其他线程可以获得锁,从而把任务进行下去,当制作了寿司,唤醒 mEmptyCondition ,客人就可以继续开心地吃寿司啦 ^_^