问题1:有两个线程,子线程先执行10次,而后主线程执行5次,而后再切换到子线程执行10,再主线程执行5次……如此往返执行50次。java
Condition将Object监视器方法(wait、notify 和 notifyAll)分解成大相径庭的对象,以便经过将这些对象与任意Lock实现组合使用,为每一个对象提供多个等待 set(wait-set)。其中,Lock 替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用。数组
因为Condition能够用来替代wait、notify等方法,因此能够对比着以前写过的线程间通讯的代码来看,来实现摘要中的问题,以前用wait和notify来实现的,如今用Condition来改写一下,代码以下:安全
package com.jie.thread.condition; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 12:41 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ public class Business { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //Condition是在具体的lock之上的 private boolean bShouldSub = true; public void sub(int i) { lock.lock(); try { while (!bShouldSub) { try { condition.await(); //用condition来调用await方法 } catch (Exception e) { e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub thread sequence of " + j + ", loop of " + i); } bShouldSub = false; condition.signal(); //用condition来发出唤醒信号,唤醒某一个 } finally { lock.unlock(); } } /** * * @author wugong * @date 2018/3/9 12:54 * @modify if true,please enter your name or update time * @param */ public void mainThreadMethod(int i) { // 锁住当前的线程,保证属性变量bShouldSub安全性 lock.lock(); try { while (bShouldSub) { try { condition.await(); //用condition来调用await方法 } catch (Exception e) { e.printStackTrace(); } } // 主线程执行5次 for (int j = 1; j <= 5; j++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } // 容许唤醒子线程 bShouldSub = true; condition.signal(); //用condition来发出唤醒信号么,唤醒某一个 } finally { lock.unlock(); } } }
package com.jie.thread.condition; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 12:40 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ public class ConditionCommunication { 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.sub(i); } } }).start(); // 主线程执行 for (int i = 1; i <= 50; i++) { bussiness.mainThreadMethod(i); } } }
从代码来看,Condition的使用时和Lock一块儿的,没有Lock就无法使用Condition,由于Condition是经过Lock来new出来的,这种用法很简单,只要掌握了synchronized和wait、notify的使用,彻底能够掌握Lock和Condition的使用。并发
上面使用Lock和Condition来代替synchronized和Object监视器方法实现了两个线程之间的通讯,如今再来写个稍微高级点应用:模拟缓冲区的阻塞队列。
什么叫缓冲区呢?举个例子,如今有不少人要发消息,我是中转站,我要帮别人把消息发出去,那么如今我 就须要作两件事,一件事是接收用户发过来的消息,并按顺序放到缓冲区,另外一件事是从缓冲区中按顺序取出用户发过来的消息,并发送出去。
如今把这个实际的问题抽象一下:缓冲区即一个数组,咱们能够向数组中写入数据,也能够从数组中把数据取走,我要作的两件事就是开启两个线程,一个存数据,一个取数据。可是问题来了,若是缓冲区满了,说明接收的消息太多了,即发送过来的消息太快了,我另外一个线程还来不及发完,致使如今缓冲区没地方放了,那么此时就得阻塞存数据这个线程,让其等待;相反,若是我转发的太快,如今缓冲区全部内容都被我发完了,尚未用户发新的消息来,那么此时就得阻塞取数据这个线程。dom
好了,分析完了这个缓冲区的阻塞队列,下面就用Condition技术来实现一下:ide
package com.jie.thread.condition.buffer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 13:04 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ public class Buffer { final Lock lock = new ReentrantLock(); //定义一个锁 final Condition notFull = lock.newCondition(); //定义阻塞队列满了的Condition final Condition notEmpty = lock.newCondition();//定义阻塞队列空了的Condition final Object[] items = new Object[10]; //为了下面模拟,设置阻塞队列的大小为10,不要设太大 int putptr, takeptr, count; //数组下标,用来标定位置的 //往队列中存数据 public void put(Object x) throws InterruptedException { lock.lock(); //上锁 try { while (count == items.length) { System.out.println(Thread.currentThread().getName() + " 被阻塞了,暂时没法存数据!"); notFull.await(); //若是队列满了,那么阻塞存数据这个线程,等待被唤醒 } //若是没满,按顺序往数组中存 items[putptr] = x; if (++putptr == items.length) //这是到达数组末端的判断,若是到了,再回到始端 putptr = 0; ++count; //消息数量 System.out.println(Thread.currentThread().getName() + "存入:" + x+";还有:"+(10-count)+"个位置"); notEmpty.signal(); //好了,如今队列中有数据了,唤醒队列空的那个线程,能够取数据啦 } finally { lock.unlock(); //放锁 } } //从队列中取数据 public Object take() throws InterruptedException { lock.lock(); //上锁 try { while (count == 0) { System.out.println(Thread.currentThread().getName() + " 被阻塞了,暂时没法取数据!"); notEmpty.await(); //若是队列是空,那么阻塞取数据这个线程,等待被唤醒 } //若是没空,按顺序从数组中取 Object x = items[takeptr]; if (++takeptr == items.length) //判断是否到达末端,若是到了,再回到始端 takeptr = 0; --count; //消息数量 System.out.println(Thread.currentThread().getName() + "取出:" + x+";还有"+(10-count)+"个位置"); notFull.signal(); //好了,如今队列中有位置了,唤醒队列满的那个线程,能够存数据啦 return x; } finally { lock.unlock(); //放锁 } } }
这个程序很经典,我从官方JDK文档中拿出来的,而后加了注释。程序中定义了两个Condition,分别针对两个线程,等待和唤醒分别用不一样的Condition来执行,思路很清晰,程序也很健壮。能够考虑一个问题,为啥要用两个Codition呢?之因此这么设计确定是有缘由的,若是用一个Condition,如今假设队列满了,可是有2个线程A和B同时存数据,那么都进入了睡眠,好,如今另外一个线程取走一个了,而后唤醒了其中一个线程A,那么A能够存了,存完后,A又唤醒一个线程,若是B被唤醒了,那就出问题了,由于此时队列是满的,B不能存的,B存的话就会覆盖原来还没被取走的值,就由于使用了一个Condition,存和取都用这个Condition来睡眠和唤醒,就乱了套。到这里,就能体会到这个Condition的用武之地了,如今来测试一下上面的阻塞队列的效果:oop
package com.jie.thread.condition.buffer; import java.util.Random; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 13:06 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ 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(); } } }
结果:测试
Thread-0存入:730;还有:9个位置 Thread-4存入:304;还有:8个位置 Thread-1存入:803;还有:7个位置 Thread-8取出:730;还有8个位置 Thread-5取出:304;还有9个位置 Thread-12取出:803;还有10个位置 Thread-9 被阻塞了,暂时没法取数据! Thread-13 被阻塞了,暂时没法取数据! Thread-3存入:351;还有:9个位置 Thread-9取出:351;还有10个位置 Thread-7 被阻塞了,暂时没法取数据! Thread-11 被阻塞了,暂时没法取数据! Thread-2存入:411;还有:9个位置 Thread-13取出:411;还有10个位置 Thread-6 被阻塞了,暂时没法取数据! Thread-10 被阻塞了,暂时没法取数据! Thread-14 被阻塞了,暂时没法取数据!
从结果中能够看出,线程5和10抢先执行,发现队列中没有,因而就被阻塞了,睡在那了,直到队列中有新的值存入才能够取,可是它们两运气很差,存的数据又被其余线程给抢先取走了,哈哈……能够多运行几回。若是想要看到存数据被阻塞,能够将取数据的线程设置少一点,这里我就不设了。this
题目2:有三个线程,子线程1先执行10次,而后子线程2执行10次,而后主线程执行5次,而后再切换到子线程1执行10次,子线程2执行10次,主线程执行5次……如此往返执行50次。spa
如过不用Condition,还真很差弄,可是用Condition来作的话,就很是方便了,原理很简单,定义三个Condition,子线程1执行完唤醒子线程2,子线程2执行完唤醒主线程,主线程执行完唤醒子线程1。唤醒机制和上面那个缓冲区道理差很少,下面看看代码吧,很容易理解。
package com.jie.thread.condition.threeBusiness; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 13:14 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ public class ThreeBusiness { 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) { 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) { 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(); } } }
package com.jie.thread.condition.threeBusiness; /** * \* Created with IntelliJ IDEA. * \* User: wugong.jie * \* Date: 2018/3/9 13:16 * \* To change this template use File | Settings | File Templates. * \* Description: * \ */ public class ThreeConditionCommunication { public static void main(String[] args) { ThreeBusiness threeBusiness = new ThreeBusiness(); new Thread(new Runnable() {// 开启一个子线程 @Override public void run() { for (int i = 1; i <= 50; i++) { threeBusiness.sub1(i); } } }).start(); new Thread(new Runnable() {// 开启另外一个子线程 @Override public void run() { for (int i = 1; i <= 50; i++) { threeBusiness.sub2(i); } } }).start(); // main方法主线程 for (int i = 1; i <= 50; i++) { threeBusiness.main(i); } } }