Condition.await()
await() 方法 造成当前线程在接到信号或被中断之前一直处于等待状态,并锁释放。
package threads; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 子线程循环10次,接着主线程循环100次,接着又回到子线程循环 10次,接着在回到主线程又循环100次,如此循环20次 */ public class ThreadTest { public static void main(String[] args) { final Busess bs = new Busess(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < 21; i++) { bs.sub2(i); } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < 21; i++) { bs.sub3(i); } } }).start(); for (int i = 1; i < 21; i++) { bs.main(i); } } } class Busess { Lock lock = new ReentrantLock();// crtl alt / Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); private int shouldSub = 1; public void sub2(int i) { lock.lock(); try { while (shouldSub != 2) { try { condition2.await(); } catch (Exception e) { } } for (int j = 0; j < 10; j++) { System.out.println("sub2: " + j); } shouldSub = 3; condition3.signal(); } finally { lock.unlock(); } } public void sub3(int i) { lock.lock(); try { while (shouldSub != 3) { try { condition3.await(); } catch (Exception e) { } } for (int j = 0; j < 20; j++) { System.out.println("sub3: " + j); } shouldSub = 1; condition1.signal(); } finally { lock.unlock(); } } public void main(int i) { lock.lock(); try { while (shouldSub != 1) { try { condition1.await(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 50; j++) { System.out.println("main: " + j); } shouldSub = 2; condition2.signal(); } finally { lock.unlock(); } } }
这个例子出处是张孝祥的视频,但是经过测试使用一个Condition 就可以。
signal方法:唤醒一个等待线程。 如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。
通过API可以知道,Condition 是选择其中一个唤醒,而不是那个对象调用唤醒那个。
修改后的代码如下
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 子线程循环10次,接着主线程循环100次,接着又回到子线程循环 10次,接着在回到主线程又循环100次,如此循环50次 */ public class ThreadTest { public static void main(String[] args) { final Busess bs = new Busess(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < 21; i++) { bs.sub2(i); } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 1; i < 21; i++) { bs.sub3(i); } } }).start(); for (int i = 1; i < 21; i++) { bs.main(i); } } } class Busess { Lock lock = new ReentrantLock();// crtl alt / Condition condition = lock.newCondition(); private int shouldSub = 1; public void sub2(int i) { lock.lock(); try { while (shouldSub != 2) { try { condition.await(); } catch (Exception e) { } } for (int j = 0; j < 10; j++) { System.out.println("sub2: " + j); } shouldSub = 3; condition.signal(); } finally { lock.unlock(); } } public void sub3(int i) { lock.lock(); try { while (shouldSub != 3) { try { condition.await(); } catch (Exception e) { } } for (int j = 0; j < 20; j++) { System.out.println("sub3: " + j); } shouldSub = 1; condition.signal(); } finally { lock.unlock(); } } public void main(int i) { lock.lock(); try { while (shouldSub != 1) { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int j = 0; j < 50; j++) { System.out.println("main: " + j); } shouldSub = 2; condition.signal(); } finally { lock.unlock(); } } }
java jdk中的一个例子,张孝祥的解释是,这两个condition 是有好处的,但是经过测试,在多个线程的时候,在堆栈满的时候,会彼此传递锁,经过测试,当4take的线程都处于等待状态,2个put的线程也处于等待的状态,take方法走完之后,2个put方法并不一定优先获得锁。 也许这么写的好处就是通知的线程找了,condition只需要通知自己管理的线程,能做到定点通知,二前程是否能优先获得到锁是另外一回事情。
package con1; import java.util.Random; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class BoundedBufferTest { public static void main(String[] args) { final BoundedBuffer buffer=new BoundedBuffer(); for (int i = 0; i < 5; i++) { new Thread(new Runnable() { @Override public void run() { while (true) { try { buffer.put(new Random().nextInt(200)); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();; } for (int i = 0; i < 5; i++) { new Thread(new Runnable() { @Override public void run() { while (true) { try { Object obj=buffer.take(); System.out.println(obj.toString()); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } System.out.println(11); } } 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;// 如果写索引写到队列的最后一个位置了,那么置为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;// 如果读索引读到队列的最后一个位置了,那么置为0 --count;// 个数-- notFull.signal();// 唤醒写线程 return x; } finally { lock.unlock(); } } }
因为两个condition是从同一个锁中获得的不同对象,所以他们彼此还是保持互斥的,
await方法在API中的介绍
void await()
造成当前线程在接到信号或被中断之前一直处于等待状态。
与此 Condition 相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下四种情况之一 以前,当前线程将一直处于休眠状态:
- 其他某个线程调用此
Condition
的signal()
方法,并且碰巧将当前线程选为被唤醒的线程; - 其他某个线程调用此
Condition
的signalAll()
方法; - 其他某个线程中断当前线程,且支持中断线程的挂起;
- 发生“虚假唤醒”
在API中也是提到,调用此condition的signal()方法,才能唤醒。这节解释了,那么些的优点是和自己推测的是一致的,样线程具有针对想的去唤醒,但是能不能得到锁的这种优先级,那就是另外一回事情了。