Condition的功能类似在传统线程技术中的Object.wait()和Object.notify()。Condition是被绑定到Lock上的,要创建一个Lock的Condition必须使用newCondtion()方法,Condition中的await()方法替换了wait()方法,signal()替换了notify()方法。
案例:子线程循环10次,接着主线程循环50次,接着又回到子线程循环10次,接着再回到主线程又循环50次,如此循环50次!
public class ConditionTest { public static void main(String[] args) { /*定义静态内部类的变量*/ final Business business = new Business(); /*创建并执行线程【执行子线程(总共是50次)】*/ new Thread(new Runnable() { @Override public void run() { for (int i=1; i<=50;i++){ business.sub(i); } } }).start(); /*调用执行主线程的方法(总共是50次)*/ for (int i=1; i<=50; i++){ business.main(i); } } static class Business { /* 定义执行的变量bool值 */ private boolean isSub = true; /* 创建一把锁 */ Lock lock = new ReentrantLock(); /* 通过锁创建Condition */ Condition condition = lock.newCondition(); /** * 子线程执行的方法 * @param i */ public void sub(int i) { /* 把关键代码锁住 */ lock.lock(); try { while (!isSub) { try { /* 如果当前还没有轮到子线程的方法执行,就进行等待 */ condition.await(); } catch (Exception e) { e.printStackTrace(); } } /* 【这里表示isSub为true,即轮到子线程执行了】遍历使得子线程中的方法执行10次 */ for (int j = 1; j <= 10; j++) { System.out.println("子线程中循环:" + j + ";总共循环:" + i); } /* 循环结束后,使得bool变量为false【轮到子线程执行】 */ isSub = false; /* 唤醒 */ condition.signal(); } finally { lock.unlock(); } } /** * 主线程执行的方法 * @param i */ public void main(int i){ /*上锁*/ lock.lock(); try { /*如果isSub为true,即轮到子线程执行,则主线程等待*/ while (isSub){ try { /*主线程等待*/ condition.await(); } catch (Exception e) { e.printStackTrace(); } /*如果主线程没有等待,就循环50次*/ for (int j=1; j<=50; j++){ System.out.println("主线程中循环:" + j +";总共循环:"+i); } } /*循环完成之后把isSub设置为true,即跳转到子线程*/ isSub = true; /*唤醒*/ condition.signal(); } finally{ lock.unlock(); } } } }
Condition的强大之处在于它可以为多个线程之间建立不同的Condition,下面是API中的一段代码:
public 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; /*读索引*/ int takeptr; /*队列中存在的数据个数*/ int count; /** * 入队列方法 * @param x */ public void put(Object x){ /*上锁*/ lock.lock(); try { while (count == items.length){ /*如果队列满了【阻塞写线程】*/ notFull.await(); } /*如果队列还没满*/ items[putptr] = x; /*如果写索引写到了队列的最后一个位置【索引要置为0】*/ if (++putptr == items.length){ putptr = 0; } /*个数自增*/ ++count; /*唤醒读线程*/ notEmpty.signal(); } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } /** * 出队列方法 * @return * @throws InterruptedException */ public Object take() throws InterruptedException{ /*上锁*/ lock.lock(); try { while (count == 0){ /*如果队列为空【阻塞读线程】*/ notEmpty.await(); } /*如果队列不为空【取值】*/ Object x = items[takeptr]; /*如果读索引读到队列的最后一个位置了,那么索引置为0*/ if (++takeptr == items.length){ takeptr = 0; } /*个数自减*/ -- count; /*唤醒写线程*/ notFull.signal(); return x; }finally{ lock.unlock(); } } }
这是一个处于多线程工作环境下的缓存区,缓存区提供了两个方法,put()和take(),put()是存数据,take()是存数据,内部有个缓存队列,这个缓存区的功能是:有多个线程往里面存数据和从里面去数据,其缓存队列(先进先出,后进后出)能缓存的最大数值为100,多个线程之间是互斥的,当缓存队列中存储的值达到100时,将写线程阻塞,并唤醒读线程,当缓存队列中存储的值为0时,将读线程阻塞,并唤醒写线程。
这段代码的执行过程:
1:一个写线程执行,调用put()方法。
2:判断count值是否为100,显然此时还么有达到。
3:继续执行,存入值。
4:判断当前写入的索引位置++后,是否和100相等,如果相等,将写入索引改为0,并将count+1.
5:仅唤醒读线程阻塞队列中的一个(这就是为什么使用了两个Condition的原因)
6:一个读线程执行,调用take()方法。
7:。。。。
8:仅唤醒写线程阻塞队列中的一个(这就是为什么使用了两个Condition的原因)。
这就是多个Condition的强大之处,假设缓存队列中已经存满,那么阻塞的肯定是写线程,唤醒的肯定是读线程,相反,阻塞的肯定是读线程,唤醒的肯定是写线程,那么假设只有一个Condition会有什么效果呢,缓存队列中已经存满,这个Lock不知道唤醒的是读线程还是写线程了,如果唤醒的是读线程,皆大欢喜,如果唤醒的是写线程,那么线程刚被唤醒,又被阻塞了,这时又去唤醒,这样就浪费了很多时间。
下面我们改进最开始的编程题:子线程1循环10次,然后子线程2循环20次,接着主线程循环50次,接着又回到子线程循环10次,然后子线程2循环20次,接着再回到主线程又循环50次,如此循环50次!
public class MultiConditionTest { public static void main(String[] args) { /* 创建Business的对象 */ final Business business = new Business(); /* 创建并启动子线程1 */ new Thread(new Runnable() { @Override public void run() { /* 总共循环50次 */ for (int i = 1; i <= 50; i++) { business.sub1(i); } } }).start(); /* 创建并执行子线程2 */ new Thread(new Runnable() { @Override public void run() { for (int i = 1; i <= 50; i++) { business.sub2(i); } } }).start(); /* 执行主线程方法 */ for (int i = 1; i <= 50; i++) { business.main(i); } } static class Business { /* 创建锁 */ Lock lock = new ReentrantLock(); /* 创建主线程的条件 */ Condition condition1 = lock.newCondition(); /* 创建执行线程2的条件 */ Condition condition2 = lock.newCondition(); /* 创建执行主线程的条件 */ Condition condition3 = lock.newCondition(); /* 某个线程执行的变量【默认子线程1执行执行】 */ private int is1Sub = 2; /** * 子线程1执行方法 * * @param i */ public void sub1(int i) { /* 上锁 */ lock.lock(); try { while (is1Sub != 2) { /* 如果不是子线程1执行【将子线程1陷入阻塞】 */ try { condition2.await(); } catch (Exception e) { e.printStackTrace(); } } /* 如果是子线程1执行【循环10次】 */ for (int j = 1; j <= 10; j++) { System.out.println("子线程1循环:" + j + ";总共循环:" + i); } /* 把变量给子线程2 */ is1Sub = 3; /* 执行完之后唤醒子线2 */ condition3.signal(); } finally { lock.unlock(); } } /** * 子线程2执行方法 * * @param i */ public void sub2(int i) { /* 上锁 */ lock.lock(); try { while (is1Sub != 3) { /* 如果不是子线程2执行【将子线程2陷入阻塞】 */ try { condition3.await(); } catch (Exception e) { e.printStackTrace(); } } /* 如果是子线程2执行【循环20次】 */ for (int j = 1; j <= 20; j++) { System.out.println("子线程2循环:" + j + ";总共循环:" + i); } /* 把变量给主线程 */ is1Sub = 1; /* 执行完之后唤醒主线程 */ condition1.signal(); } finally { lock.unlock(); } } /** * 主线程执行方法 * * @param i */ public void main(int i) { /* 上锁 */ lock.lock(); try { while (is1Sub != 1) { /* 如果不是主线程执行【将主线程陷入阻塞】 */ try { condition1.await(); } catch (Exception e) { e.printStackTrace(); } } /* 如果是主线程执行【循环50次】 */ for (int j = 1; j <= 50; j++) { System.out.println("主线程循环:" + j + ";总共循环:" + i); } /* 把变量给主线程 */ is1Sub = 2; /* 执行完之后唤醒子线程1 */ condition2.signal(); } finally { lock.unlock(); } } } }