JUC锁-Condition(三)
Condition介紹
Condition的作用是對鎖進行更精確的控制。Condition中的await()方法相當于Object的wait()方法,Condition中的signal()方法相當于Object的notify()方法,Condition中的signalAll()相當于Object的notifyAll()方法。
不同的是,Object中的wait(),notify(),notifyAll()方法是和”同步鎖”(synchronized關鍵字)捆綁使用的;而Condition是需要與”互斥鎖”/”共享鎖”捆綁使用的。
Condition的使用介紹
- 示例1是通過Object的wait(), notify()來演示線程的休眠/喚醒功能。
示例2是通過Condition的await(), signal()來演示線程的休眠/喚醒功能。
示例1 2展示了兩者的相似之處。
- 示例3是通過Condition的高級功能
Object的wait,notify方法
public class WaitTest1 {public static void main(String[] args) {ThreadA ta = new ThreadA("ta");synchronized(ta) { // 通過synchronized(ta)獲取“對象ta的同步鎖”try {System.out.println(Thread.currentThread().getName()+" start ta");ta.start();System.out.println(Thread.currentThread().getName()+" block");ta.wait(); // 等待System.out.println(Thread.currentThread().getName()+" continue");} catch (InterruptedException e) {e.printStackTrace();}}}static class ThreadA extends Thread{public ThreadA(String name) {super(name);}public void run() {synchronized (this) { // 通過synchronized(this)獲取“當前對象的同步鎖”System.out.println(Thread.currentThread().getName()+" wakup others");notify(); // 喚醒“當前對象上的等待線程”}}} }Condition的await,signal方法
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;public class ConditionTest1 {private static Lock lock = new ReentrantLock();private static Condition condition = lock.newCondition();public static void main(String[] args) {ThreadA ta = new ThreadA("ta");lock.lock(); // 獲取鎖try {System.out.println(Thread.currentThread().getName()+" start ta");ta.start();System.out.println(Thread.currentThread().getName()+" block");condition.await(); // 等待System.out.println(Thread.currentThread().getName()+" continue");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock(); // 釋放鎖}}static class ThreadA extends Thread{public ThreadA(String name) {super(name);}public void run() {lock.lock(); // 獲取鎖try {System.out.println(Thread.currentThread().getName()+" wakup others");condition.signal(); // 喚醒“condition所在鎖上的其它線程”} finally {lock.unlock(); // 釋放鎖}}} }從上面兩個例子,我們可以看出來Object的notify,wait方法實際上用Condition一樣可以實現。但是這就意味著兩個東西一樣嗎?
它更強大的地方在于:能夠更加精細的控制多線程的休眠與喚醒。對于同一個鎖,我們可以創建多個Condition,在不同的情況下使用不同的Condition。
例如,假如多線程讀/寫同一個緩沖區:
當向緩沖區中寫入數據之后,喚醒”讀線程”;當從緩沖區讀出數據之后,喚醒”寫線程”;并且當緩沖區滿的時候,”寫線程”需要等待;當緩沖區為空時,”讀線程”需要等待。
如果采用Object類中的wait(), notify(), notifyAll()實現該緩沖區,當向緩沖區寫入數據之后需要喚醒”讀線程”時,不可能通過notify()或notifyAll()明確的指定喚醒”讀線程”,而只能通過notifyAll喚醒所有線程(但是notifyAll無法區分喚醒的線程是讀線程,還是寫線程)。
但是,通過Condition,就能明確的指定喚醒讀線程。
看看下面的示例3,可能對這個概念有更深刻的理解。
Condition高級特性的例子:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock;class BoundedBuffer {final Lock lock = new ReentrantLock();final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[5];int putptr, takeptr, count;public void put(Object x) throws InterruptedException {lock.lock(); //獲取鎖try {// 如果“緩沖已滿”,則等待;直到“緩沖”不是滿的,才將x添加到緩沖中。while (count == items.length)notFull.await();// 將x添加到緩沖中items[putptr] = x; // 將“put統計數putptr+1”;如果“緩沖已滿”,則設putptr為0。if (++putptr == items.length) putptr = 0;// 將“緩沖”數量+1++count;// 喚醒take線程,因為take線程通過notEmpty.await()等待notEmpty.signal();// 打印寫入的數據System.out.println(Thread.currentThread().getName() + " put "+ (Integer)x);} finally {lock.unlock(); // 釋放鎖}}public Object take() throws InterruptedException {lock.lock(); //獲取鎖try {// 如果“緩沖為空”,則等待;直到“緩沖”不為空,才將x從緩沖中取出。while (count == 0) notEmpty.await();// 將x從緩沖中取出Object x = items[takeptr]; // 將“take統計數takeptr+1”;如果“緩沖為空”,則設takeptr為0。if (++takeptr == items.length) takeptr = 0;// 將“緩沖”數量-1--count;// 喚醒put線程,因為put線程通過notFull.await()等待notFull.signal();// 打印取出的數據System.out.println(Thread.currentThread().getName() + " take "+ (Integer)x);return x;} finally {lock.unlock(); // 釋放鎖}} }public class ConditionTest2 {private static BoundedBuffer bb = new BoundedBuffer();public static void main(String[] args) {// 啟動10個“寫線程”,向BoundedBuffer中不斷的寫數據(寫入0-9);// 啟動10個“讀線程”,從BoundedBuffer中不斷的讀數據。for (int i=0; i<10; i++) {new PutThread("p"+i, i).start();new TakeThread("t"+i).start();}}static class PutThread extends Thread {private int num;public PutThread(String name, int num) {super(name);this.num = num;}public void run() {try {Thread.sleep(1); // 線程休眠1msbb.put(num); // 向BoundedBuffer中寫入數據} catch (InterruptedException e) {}}}static class TakeThread extends Thread {public TakeThread(String name) {super(name);}public void run() {try {Thread.sleep(10); // 線程休眠1msInteger num = (Integer)bb.take(); // 從BoundedBuffer中取出數據} catch (InterruptedException e) {}}} }運行結果:
p1 put 1 p4 put 4 p5 put 5 p0 put 0 p2 put 2 t0 take 1 p3 put 3 t1 take 4 p6 put 6 t2 take 5 p7 put 7 t3 take 0 p8 put 8 t4 take 2 p9 put 9 t5 take 3 t6 take 6 t7 take 7 t8 take 8 t9 take 9說明:
(01) BoundedBuffer 是容量為5的緩沖,緩沖中存儲的是Object對象,支持多線程的讀/寫緩沖。多個線程操作“一個BoundedBuffer對象”時,它們通過互斥鎖lock對緩沖區items進行互斥訪問;而且同一個BoundedBuffer對象下的全部線程共用“notFull”和“notEmpty”這兩個Condition。
notFull用于控制寫緩沖,notEmpty用于控制讀緩沖。
當緩沖已滿的時候,調用put的線程會執行notFull.await()進行等待;
當緩沖區不是滿的狀態時,就將對象添加到緩沖區并將緩沖區的容量count+1,最后,調用notEmpty.signal()緩沖notEmpty上的等待線程(調用notEmpty.await的線程)。
簡言之,notFull控制“緩沖區的寫入”,當往緩沖區寫入數據之后會喚醒notEmpty上的等待線程。
同理,notEmpty控制“緩沖區的讀取”,當讀取了緩沖區數據之后會喚醒notFull上的等待線程。
總結
以上是生活随笔為你收集整理的JUC锁-Condition(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JUC锁-互斥锁ReentrantLoc
- 下一篇: JUC锁-LockSupport(四)