可以考虑一个问题,为啥要用两个 Condition 呢?之所以这么设计肯定是有原因的:如果用一个 Condition,现在假设队列满了,但是有 2 个线程 A 和 B 同时存数据,那么都进入了睡眠,好,现在另一个线程取走一个了,然后唤醒了其中一个线程 A,那么 A 可以存了,存完后, A 又唤醒一个线程,如果 B 被唤醒了,那就出问题了,因为此时队列是满的,B 不能存的,B存的话就会覆盖原来还没被取走的只,这就是一个 Condition 带来的问题。
来测试一下上面的阻塞队列的效果。
public class BoundedBuffer { public static void main(String[] args) { Buffer buffer = new Buffer(); for(int i = 0; i < 5; i ++) { //开启5个线程往缓冲区存数据 new Thread(new Runnable() { @Override public void run() { try { buffer.put(new Random().nextInt(1000)); //随机存数据 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } for(int i = 0; i < 10; i ++) { //开启10个线程从缓冲区中取数据 new Thread(new Runnable() { @Override public void run() { try { buffer.take(); //从缓冲区取数据 } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } }
我故意只开启 5 个线程存数据,10 个线程取数据,就是想让它出现取数据被阻塞的情况发生,看运行的结果。
Thread-5 被阻塞了,暂时无法取数据!
Thread-10 被阻塞了,暂时无法取数据!
Thread-1 存好了值: 755
Thread-0 存好了值: 206
Thread-2 存好了值: 741
Thread-3 存好了值: 381
Thread-14 取出了值: 755
Thread-4 存好了值: 783
Thread-6 取出了值: 206
Thread-7 取出了值: 741
Thread-8 取出了值: 381
Thread-9 取出了值: 783
Thread-5 被阻塞了,暂时无法取数据!
Thread-11 被阻塞了,暂时无法取数据!
Thread-12 被阻塞了,暂时无法取数据!
Thread-10 被阻塞了,暂时无法取数据!
Thread-13 被阻塞了,暂时无法取数据!
从结果中可以看出,线程 5 和10 抢先执行,发现队列中没有,于是就被阻塞了,睡在那了,知道队列中有新的值存入才可以取,但是他们两运气不好,存的数据又被其他线程前线取走了……可以多运行几次。如果想要看到存数据被阻塞,可以将取数据的线程设置少一点,这里我就不设了。
2.2 两个以上线程之间的唤醒
还是原来那个题目,现在让三个线程来执行,看一下题目:
有三个线程,子线程1先执行10次,然后子线程2执行10次,然后主线程执行5次,然后再切换到子线程1执行10次,子线程2执行10次,主线程执行5次……如此往返执行50次。
如果不用 Condition,还真不好弄,但是用 Condition 来做的话,就非常方便了,原理很简单,定义三个 Condition,子线程 1 执行完唤醒子线程 2,子线程 2 执行完唤醒主线程,主线程执行完唤醒子线程1。唤醒机制和上面那个缓冲区道理差不多,下面附上代码。
public class ThreeConditionCommunication { public static void main(String[] args) { Business bussiness = new Business(); new Thread(new Runnable() {// 开启一个子线程 @Override public void run() { for (int i = 1; i <= 50; i++) { bussiness.sub1(i); } } }).start(); new Thread(new Runnable() {// 开启另一个子线程 @Override public void run() { for (int i = 1; i <= 50; i++) { bussiness.sub2(i); } } }).start(); // main方法主线程 for (int i = 1; i <= 50; i++) { bussiness.main(i); } } static class Business { Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); //Condition是在具体的lock之上的 Condition condition2 = lock.newCondition(); Condition conditionMain = lock.newCondition(); private int bShouldSub = 0; public void sub1(int i) { lock.lock(); try { while (bShouldSub != 0) { try { condition1.await(); //用condition来调用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub1 thread sequence of " + j + ", loop of " + i); } bShouldSub = 1; condition2.signal(); //让线程2执行 } finally { lock.unlock(); } } public void sub2(int i) { lock.lock(); try { while (bShouldSub != 1) { try { condition2.await(); //用condition来调用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub2 thread sequence of " + j + ", loop of " + i); } bShouldSub = 2; conditionMain.signal(); //让主线程执行 } finally { lock.unlock(); } } public void main(int i) { lock.lock(); try { while (bShouldSub != 2) { try { conditionMain.await(); //用condition来调用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 5; j++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } bShouldSub = 0; condition1.signal(); //让线程1执行 } finally { lock.unlock(); } } } }
关于线程中 Condition 技术就分享这么多吧。