一.需求
实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次.
即:A->B->C---A->B->C---A->B->C
二.实现
1.分析
在前面文章java核心知识点学习----多线程并发之线程间的通信,notify,wait,曾实现过需求两个线程间隔循环的例子.涉及到3个线程就使用之间的方法就有点麻烦了,这里借着刚学的Lock锁可以很方便实现互斥,但如何实现三个线程间的通信呢?
2.实现效果
3.实现代码
package com.amos.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @ClassName: ThreadSynchronizedConnect * @Description:实现线程间的通信,需求:主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次.A->B->C---A->B->C---A->B->C * @author: amosli * @email:hi_amos@outlook.com * @date Apr 20, 2014 4:39:44 PM */ public class ThreeConnect { public static void main(String[] args) { final Business business = new Business(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 3; i++) { business.sub2(i); } } }).start(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 3; i++) { business.sub3(i); } } }).start(); for (int i = 0; i < 3; i++) { business.main(i); } } static class Business { Lock lock = new ReentrantLock(); Condition conditionmain = lock.newCondition(); Condition conditionsub2 = lock.newCondition(); Condition conditionsub3 = lock.newCondition(); private int current = 1; // 子方法2 public void sub2(int i) { lock.lock(); try { if (current != 2) {// 如果不为true,将等待,Blocked状态 try { conditionsub2.await(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 2; j++) { System.out.println("sub2 thread:" + j + " loop:" + i); } current = 3; conditionsub3.signal();// 唤醒3 } finally { lock.unlock(); } } // 子方法3 public void sub3(int i) { lock.lock(); try { if (current != 3) {// 如果不为true,将等待,Blocked状态 try { conditionsub3.await(); } catch (Exception e) { e.printStackTrace(); } } for (int j = 0; j < 2; j++) { System.out.println("sub3 thread:" + j + " loop:" + i); } current = 1; conditionmain.signal(); } finally { lock.unlock(); } } // 主方法 public void main(int i) { lock.lock(); try { if (current != 1) { try { conditionmain.await(); } catch (Exception e) { e.printStackTrace(); } } for (int j = 0; j < 3; j++) { System.out.println("main thread:" + j + " loop:" + i); } current = 2; conditionsub2.signal(); } finally { lock.unlock(); } } } }
4.代码说明
上面的代码中用的是Lock进行加锁操作的,然后线程间的通信没有用之前的wait(),notify()方法用的是Conditon的await()和signal()
为什么要使用Condition??
如果程序中不使用synchronized关键字来保证同步,而是使用Lock对象来保证数据同步,则系统中不存在隐式的同步监视器,也就不能使用wait().notify()方法进行线程通信了.
因为使用了Lock对象,所以要使线程间通信,可以使用Condition进行控制线程间通信.
Condition将同步监视器方法(wait(),notify(),notifyall()等)分解成截然不同的对象,以便通过将这些对象与Lock对象组合使用,为每个对象提供多个等待集(wait-set).
创建一个Condition,只需要lock.newCondition()即可,lock是已经new 好的ReentrantLock()对象.
>>await()方法与wait()功能类似,都是将线程加入到阻塞状态.
>>signal()方法与notify()方法类似,都是唤醒等待中的线程,只是signal()方法可以指定具体要唤醒的线程.
>>signalAll()方法与notifyAll()方法类似,都是唤醒所有等待中的线程.
5.扩展
1).下面是简单的对比,notify和signal等方法的对比,其效果是完全一样的.
package com.amos.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @ClassName: ConditionConnect * @Description:用condition替代wait,notify实现线程间的通信,需求:子线程循环10次,主线程循环100次,这样间隔循环50次. * @author: amosli * @email:hi_amos@outlook.com * @date Apr 24, 2014 12:07:23 AM */ public class ConditionConnect { public static void main(String[] args) { final Business business = new Business(); new Thread(new Runnable() { public void run() { for (int i = 0; i < 5; i++) { business.sub(i); } } }).start(); for (int i = 0; i < 5; i++) { business.main(i); } } /* * 经验:要用到共同数据(包括同步锁)的若干方法,应该归在同一个类身上,这样方便实现,高类聚和程序的健状性上. */ static class Business { private boolean is_sub = true; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 子方法 public void sub(int i) { lock.lock(); try { while (!is_sub) {// 如果不为true,将等待,Blocked状态 try { // this.wait(); condition.await(); } catch (Exception e) { e.printStackTrace(); } } for (int j = 0; j < 2; j++) { System.out.println("sub thread:" + j + " loop:" + i); } is_sub = false; // this.notify();//唤醒正在等待的线程 condition.signal(); } finally { lock.unlock(); } } // 主方法 public void main(int i) { lock.lock(); try { while (is_sub) { try { // this.wait(); condition.await(); } catch (Exception e) { e.printStackTrace(); } } for (int j = 0; j < 10; j++) { System.out.println("main thread:" + j + " loop:" + i); } is_sub = true; // this.notify(); condition.signal(); } finally { lock.unlock(); } } } }
2).官方提供的例子
做了简单的修改:
先看效果:
再看代码:
package com.amos.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @ClassName: ConditionTest * @Description: 官方提供的例子 * @author: amosli * @email:hi_amos@outlook.com * @date Apr 24, 2014 12:40:58 AM */ public class ConditionTest { public static void main(String[] args) throws Exception { BoundedBuffer buffer = new BoundedBuffer(); buffer.put("hi_amos"); for(int i=0;i<3;i++){ buffer.put(i); System.out.println("take:"+buffer.take()); } } static class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; //设值 public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++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(); } } } }
思考:例子中为什么要new 2个condition??