1. 程式人生 > >Java多執行緒學習筆記17之Lock的使用

Java多執行緒學習筆記17之Lock的使用

詳細程式碼見:github程式碼地址

 

本節內容:

1) Lock/ReentrantLock的newCondition()方法及Condition類/await、signal方法文件翻譯

2) Condition實現等待/通知使用(錯誤示例及正確示例)

3) 使用多個Condition實現通知部分執行緒

4) 實現消費者和生產者模型(利用await()及signal()方法)

 

 2.使用Condition實現等待/通知

 概述:
     關鍵字synchronized與wait()和notify()/notifyAll()方法相結合可以實現等待/通知
模式,類ReentrantLock也可以實現同樣的功能,但需要藉助於Condition物件。Condition
類是JDK5中出現的技術,使用它有更好的靈活性,比如可以實現多路通知功能,也就是在一個
Lock物件裡面可以建立多個Condition(即物件監視器)例項,執行緒物件可以註冊在指定的
Condition中,從而可以有選擇性地進行執行緒通知,在排程執行緒上更加靈活。

synchronized等待通知和ReentrantLock等待通知的區別:

     在使用notify/notifyAll方法進行通知時,被通知的執行緒由JVM隨機選擇。但使用ReentrantLock
結合Condition類是可以實現"選擇性通知".
    synchronized就相當於整個Lock物件中只有一個單一的Condition物件,所有的執行緒都注
冊到它一個物件的身上。執行緒開始notifyAll()時,需要通知所有的WAITING執行緒,沒有選擇權,
會出現一定的效率問題。


(1) Lock/ReentrantLock的newCondition()方法及Condition類/await、signal方法


注:
    condition物件是通過實現了Lock介面的具體鎖物件來呼叫newCondition方法實現的。因此我們
有必要檢視Lock介面的newCondition方法以及ReentrantLock下的newCondtion方法,此外還有一些
讀寫鎖也實現了Lock的newCondition方法。

文件翻譯

1) Lock和ReentrantLock的newConditon()

Lock newCondition():
Condition newCondition​()
Returns a new Condition instance that is bound to this Lock instance.
Before waiting on the condition the lock must be held by the current thread. A call to 
Condition.await() will atomically release the lock before waiting and re-acquire 
the lock before the wait returns.
返回繫結到此鎖例項的新條件例項。在等待條件之前,鎖必須由當前執行緒持有。呼叫wait()在等待和重新
獲得鎖之前自動釋放鎖。

Implementation Considerations

The exact operation of the Condition instance depends on the Lock implementation 
and must be documented by that implementation.
實現注意事項

Condition例項的確切操作取決於鎖實現。並且必須由該實現記錄。

Returns:
A new Condition instance for this Lock instance 返回Lock例項的新的Condition例項
Throws:
UnsupportedOperationException - if this Lock implementation does not support conditions
如果這個Lock介面的實現不支援conditons,那麼丟擲不支援操縱異常


ReentrantLock newCondition():
public Condition newCondition​()
Returns a Condition instance for use with this Lock instance.
The returned Condition instance supports the same usages as do the Object monitor methods 
(wait, notify, and notifyAll) when used with the built-in monitor lock.
返回用於Lock例項的一個Condition例項
返回的條件例項支援與當使用內建監視器物件的物件監視器方法相同的用法(wait,notify,notifyAll)

If this lock is not held when any of the Condition waiting or signalling methods are 
called, then an IllegalMonitorStateException is thrown.
When the condition waiting methods are called the lock is released and, before they 
return, the lock is reacquired and the lock hold count restored to what it was when 
the method was called.
如果當任何Conditition物件等待或通知方法呼叫的時候不持有鎖,那麼就丟擲IllegalMonitorStateException
異常。當condition物件的wait()方法被呼叫的時候鎖會被釋放,在該方法返回之前,鎖被重新獲取,鎖計數器恢復到
原來的狀態。
If a thread is interrupted while waiting then the wait will terminate, an 
InterruptedException will be thrown, and the thread's interrupted status will be cleared.
Waiting threads are signalled in FIFO order.
The ordering of lock reacquisition for threads returning from waiting methods 
is the same as for threads initially acquiring the lock, which is in the default
case not specified, but for fair locks favors those threads that have been 
waiting the longest.
如果當呼叫wait()方法等待鎖的時候被執行緒被中斷,則丟擲InterruptedException異常,同時執行緒
的中斷狀態將被清除。
等待執行緒以先進先出的順序被通知。
從wait()方法返回的執行緒重新獲得鎖的順序與執行緒開始的時候獲得鎖的順序一樣,如果不指定預設是
這樣子。但是對於公平鎖而言,更傾向於那些已經等待最長時間的執行緒獲得鎖。

2) Condition類/await、signal方法

Condition類:
public interface Condition
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct 
objects to give the effect of having multiple wait-sets per object, by combining them with 
the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized 
methods and statements, a Condition replaces the use of the Object monitor methods.
Conditions (also known as condition queues or condition variables) provide a means for 
one thread to suspend execution (to "wait") until notified by another thread that some 
state condition may now be true. Because access to this shared state information occurs 
in different threads, it must be protected, so a lock of some form is associated with 
the condition. The key property that waiting for a condition provides is that it atomically 
releases the associated lock and suspends the current thread, just like Object.wait.
條件因素將物件監視器方法(wait, notify和notifyAll)排除到不同的物件中,通過將它們與任意鎖實現相結合,
使每個物件具有多個等待集的效果。Lock物件替代了synchronized方法及同步語句塊的使用,一個Condition
物件替代了物件監視器方法的使用。Conditions(也稱為條件佇列或條件變數)為一個執行緒提供了暫停執行("等待")
的方法,直到某個狀態條件為真時另一個執行緒通知。由於對這個共享狀態資訊的訪問發生在不同的執行緒中,因此必須
對其進行保護,因此必須與某種形式的鎖相關聯的條件。一個condition物件提供的wait方法的關鍵屬性是自動釋放關聯
的鎖並掛起當前的執行緒(即await()方法的作用),類似Object.wait()的作用

A Condition instance is intrinsically bound to a lock. To obtain a Condition instance for 
a particular Lock instance use its newCondition() method.
一個Condition例項本質上繫結一個lock例項.要獲取一特定Lock例項的一個Condition例項可以使用它的new
Conditon()方法

As an example, suppose we have a bounded buffer which supports put and take methods. If 
a take is attempted on an empty buffer, then the thread will block until an item becomes 
available; if a put is attempted on a full buffer, then the thread will block until a 
space becomes available. We would like to keep waiting put threads and take threads in 
separate wait-sets so that we can use the optimization of only notifying a single thread 
at a time when items or spaces become available in the buffer. This can be achieved 
using two Condition instances.
例如,假設我們有一個支援put和take方法的有界緩衝區。如果在空緩衝區上嘗試執行take方法,則執行緒將
阻塞,直到某項可用為止;如果想要在一個滿了的緩衝區呼叫put方法,那麼執行緒將阻塞,直到空間可用為止。
這個可以通過使用兩個Condition例項來實現。

 class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   /**
   * putptr: 是陣列中將要放入的索引
   * takeptr: 是陣列中將要取走的索引
   * count是記錄陣列中有多少個有效元素
   * 這裡我的理解是應該給他們都加上volatile,這樣執行緒之間可見。
   * 之前我的理解有誤,因為synchronized和這些lock.lock實際上保證了count的可見性,即
   * 程序公共堆疊中取值,而不是從執行緒私有堆疊中取值
   */
   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();
     }
   }
 }
 
(The ArrayBlockingQueue class provides this functionality, so there is no reason 
to implement this sample usage class.)
A Condition implementation can provide behavior and semantics that is different 
from that of the Object monitor methods, such as guaranteed ordering for notifications, 
or not requiring a lock to be held when performing notifications. If an implementation
provides such specialized semantics then the implementation must document those semantics.
(ArrayBlockingQueue類提供了這個功能,所以沒有理由實現這個示例用法類)
一個Conditon實現可以提供與物件監視器方法不同的行為和語義,比如保證通知的順序,或者在執行通知時不需
要持有鎖。如果實現提供了這樣的專用語義,那麼實現必須記錄這些語義。

Note that Condition instances are just normal objects and can themselves be used as 
the target in a synchronized statement, and can have their own monitor wait and notify 
methods invoked. Acquiring the monitor lock of a Condition instance, or using its 
monitor methods, has no specified relationship with acquiring the Lock associated 
with that Condition or the use of its waiting and signalling methods. It is recommended 
that to avoid confusion you never use Condition instances in this way, except perhaps 
within their own implementation.
注意,Condition例項只是普通物件,它們本身可以在同步語句中用作目標,並且可以呼叫它們自己的監視器
等待和通知方法。獲取一個Condition例項的監視器鎖或使用它們的監視器方法,與獲取與該條件相關的鎖或使
用其等待和通知方法沒有特定的關係。建議為了避免混淆,永遠不要以這種方式使用條件例項,除非是在它們
自己的實現中。

Except where noted, passing a null value for any parameter will result in a 
NullPointerException being thrown.
為任何引數傳遞空值將導致丟擲NullPointerException異常

Implementation Considerations

When waiting upon a Condition, a "spurious wakeup" is permitted to occur, in 
general, as a concession to the underlying platform semantics. This has little 
practical impact on most application programs as a Condition should always be 
waited upon in a loop, testing the state predicate that is being waited for. 
An implementation is free to remove the possibility of spurious wakeups but it 
is recommended that applications programmers always assume that they can occur 
and so always wait in a loop.
當一個Condition呼叫wait方法之後,通常允許發生"虛假喚醒",作為對底層平臺語義的讓步,這對
大多數應用程式沒有什麼實際上的影響,因為wait呼叫應總是發生在一個迴圈中,檢測正在等待的狀態
。可以自由的實現來排除虛假喚醒的可能性,但建議應用程式程式設計師始終假定它們可能發生,所以總是
在迴圈中等待。

The three forms of condition waiting (interruptible, non-interruptible, and timed) 
may differ in their ease of implementation on some platforms and in their performance 
characteristics. In particular, it may be difficult to provide these features and 
maintain specific semantics such as ordering guarantees. Further, the ability to 
interrupt the actual suspension of the thread may not always be feasible to 
implement on all platforms.
條件的等待的三種形式(可中斷的,不可中斷的和定時的)在某些平臺上實現的便捷性和效能特性上可能有所
不同。特別是,提供這些特性和維護特定的語義(比如順序保證)可能會很困難。此外,在所有平臺上實現
中斷正在掛起的執行緒的能力並不總是可行的。

Consequently, an implementation is not required to define exactly the same guarantees 
or semantics for all three forms of waiting, nor is it required to support 
interruption of the actual suspension of the thread.
因此,實現不需要為所有三種等待形式定義完全相同的保證或語義,也不需要支援中斷執行緒的實際掛起。

An implementation is required to clearly document the semantics and guarantees 
provided by each of the waiting methods, and when an implementation does support 
interruption of thread suspension then it must obey the interruption semantics as
defined in this interface.
需要實現清除地記錄每個等待方法提供的語義和保證

As interruption generally implies cancellation, and checks for interruption are often 
infrequent, an implementation can favor responding to an interrupt over normal method 
return. This is true even if it can be shown that the interrupt occurred after another 
action that may have unblocked the thread. An implementation should document this behavior.
由於中斷通常意味著取消,而且檢查中斷的頻率很低,因此實現可能更傾向於響應中斷而不是正常的方法返回。
即使它可以顯示中斷髮生在另一個動作之後,可能已經解除了執行緒的阻塞,實現應該記錄這種行為。


這裡只翻譯await()和signal()方法,其他的方法大同小異
await():
void await​()
    throws InterruptedException
Causes the current thread to wait until it is signalled or interrupted.
The lock associated with this Condition is atomically released and the current thread 
becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:
導致當前執行緒等待,直到它被通知或中斷。
與此Condition物件關聯的lock被原子地釋放,當前執行緒處於執行緒排程目的而被禁用,並處於休眠狀態,直到發生
以下四種情況之一:

Some other thread invokes the signal() method for this Condition and the current thread 
happens to be chosen as the thread to be awakened; or
Some other thread invokes the signalAll() method for this Condition; or
Some other thread interrupts the current thread, and interruption of thread suspension 
is supported; or
A "spurious wakeup" occurs.
In all cases, before this method can return the current thread must re-acquire the lock 
associated with this condition. When the thread returns it is guaranteed to hold this lock.
其他一些執行緒呼叫了這個Condition物件的signal()方法,而當前執行緒恰好被選擇為要喚醒的執行緒;或其他一些
執行緒呼叫這個Condition物件的signalAll()方法;或其他的執行緒中斷了當前執行緒,並且中斷掛起的執行緒是支援的;
或者一個"虛假喚醒"發生了。在所有的情況下,在此方法返回之前,當前執行緒必須重新獲取與Condition相關聯
的lock鎖。當執行緒返回時,它保證持有這個鎖。

If the current thread:
has its interrupted status set on entry to this method; or
is interrupted while waiting and interruption of thread suspension is supported,
then InterruptedException is thrown and the current thread's interrupted status is cleared. 
It is not specified, in the first case, whether or not the test for interruption occurs 
before the lock is released.
如果當前執行緒在進入此方法時設定了中斷狀態;或支援在wait()方法呼叫期間中斷以及線上程掛起時中斷,那麼
丟擲InterrupedException,同時當前執行緒的中斷狀態被清除。在第一情況下,不論在釋放鎖之前是否進行中斷
測試。

Implementation Considerations
The current thread is assumed to hold the lock associated with this Condition when this 
method is called. It is up to the implementation to determine if this is the case and if 
not, how to respond. Typically, an exception will be thrown (such as IllegalMonitorStateException) 
and the implementation must document that fact.
當await()方法被呼叫,假定當前執行緒持有與此Condition例項相關聯的鎖。這取決於實現,以確定這是否是事實,如果不是,
如何響應。通常會丟擲(IllegalMonitorStateException),實現必須記錄這個事實。

An implementation can favor responding to an interrupt over normal method return in 
response to a signal. In that case the implementation must ensure that the signal is 
redirected to another waiting thread, if there is one.
實現可以支援對中斷的響應而不是通過正常方法返回。在這種情況下,實現必須確保signal會通知的執行緒被
重定向到另一個等待執行緒(如果有的話)

Throws:
InterruptedException - if the current thread is interrupted (and interruption of 
thread suspension is supported)


signal():
void signal​()
Wakes up one waiting thread.
If any threads are waiting on this condition then one is selected for waking up. That 
thread must then re-acquire the lock before returning from await.
喚醒一個正在等待的執行緒
如果有執行緒正在condition下等待,那麼將選擇一個執行緒進行喚醒。執行緒在等待返回之前必須重新獲取鎖。

Implementation Considerations

An implementation may (and typically does) require that the current thread hold the 
lock associated with this Condition when this method is called. Implementations must 
document this precondition and any actions taken if the lock is not held. Typically, 
an exception such as IllegalMonitorStateException will be thrown.
當呼叫此方法時,實現可能需要當前執行緒持有與此條件相關聯的鎖(通常是這樣).實現筆錄記錄這個先決條件
以及如果鎖不持有鎖採取的操作。通常,會丟擲一個異常,例如IllegalMonitorStateException異常。

 

(2) 錯誤用法與解決
舉例1:

package chapter04.section01.thread_4_1_3.project_1_UseConditionWaitNotifyError;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void await() {
		try {
			condition.await();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}


package chapter04.section01.thread_4_1_3.project_1_UseConditionWaitNotifyError;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.await();
	}
}


package chapter04.section01.thread_4_1_3.project_1_UseConditionWaitNotifyError;

public class Run {
	public static void main(String[] args) {
		MyService service = new MyService();
		
		ThreadA a = new ThreadA(service);
		a.start();
	}
}
/*
result:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
	at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(Unknown Source)
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
	at chapter04.section01.thread_4_1_3.project_1_UseConditionWaitNotifyError.MyService.await(MyService.java:13)
	at chapter04.section01.thread_4_1_3.project_1_UseConditionWaitNotifyError.ThreadA.run(ThreadA.java:13)
*/

結果分析:
可以看到監視器出錯,要在condition.wait()方法呼叫之前用lock.lock()程式碼獲得同步監
視器


舉例2:

package chapter04.section01.thread_4_1_3.project_2_z3ok;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void await() {
		try {
			lock.lock(); 
			System.out.println("A");
			condition.await();
			System.out.println("B");
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
			System.out.println("鎖釋放了!");
		}
	}
}


package chapter04.section01.thread_4_1_3.project_2_z3ok;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.await();
	}
}


package chapter04.section01.thread_4_1_3.project_2_z3ok;

public class Run {
	public static void main(String[] args) {
		MyService service = new MyService();
		
		ThreadA a = new ThreadA(service);
		a.start();
	}
}
/*
A
*/

結果分析: 呼叫了Condition物件的await()方法,使當前執行任務的執行緒進入了WAITING狀
態,但是沒有喚醒


(3) 正確使用Condition實現等待/通知
 

package chapter04.section01.thread_4_1_4.project_1_UseConditionWaitNotifyOk;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void await() {
		try {
			lock.lock(); 
			System.out.println(" await時間為" + System.currentTimeMillis());
			condition.await();
			System.out.println("B");
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
			System.out.println("鎖釋放了!");
		}
	}
	
	public void signal() {
		try {
			lock.lock();
			System.out.println("signal時間為" + System.currentTimeMillis());
			condition.signal();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			lock.unlock();
		}
	}
}


package chapter04.section01.thread_4_1_4.project_1_UseConditionWaitNotifyOk;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.await();
	}
}


package chapter04.section01.thread_4_1_4.project_1_UseConditionWaitNotifyOk;

public class Run {
	public static void main(String[] args) throws InterruptedException{
		MyService service = new MyService();
		
		ThreadA a = new ThreadA(service);
		a.start();
		
		Thread.sleep(3000);
		
		service.signal();
	}
}
/*
result:
 await時間為1540884494515
signal時間為1540884497516
B
鎖釋放了!
*/


(4) 使用多個Condition實現通知部分執行緒
錯誤用法舉例:

package chapter04.section01.thread_4_1_5.project_1_MustUseMoreCondition_Error;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	
	public void awaitA() {
		try {
			lock.lock();
			System.out.println("begin awaitA時間為" + System.currentTimeMillis()
			+ " ThreadName=" + Thread.currentThread().getName());
			condition.await();
			System.out.println("  end awaitA時間為" + System.currentTimeMillis() 
			+ " ThreadName=" + Thread.currentThread().getName());
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void awaitB() {
		try {
			lock.lock();
			System.out.println("begin awaitB時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
			condition.await();
			System.out.println("  end awaitB時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void signalAll() {
		try {
			lock.lock();
			System.out.println("  signalAll時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
			condition.signalAll();
		} catch (Exception e) {
			// TODO: handle exception
		} finally {
			lock.unlock();
		}
	}
}


package chapter04.section01.thread_4_1_5.project_1_MustUseMoreCondition_Error;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		service.awaitA();
	}
}


package chapter04.section01.thread_4_1_5.project_1_MustUseMoreCondition_Error;

public class ThreadB extends Thread {

	private MyService service;

	public ThreadB(MyService service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		service.awaitB();
	}
}


package chapter04.section01.thread_4_1_5.project_1_MustUseMoreCondition_Error;

public class Run {

	public static void main(String[] args) throws InterruptedException {

		MyService service = new MyService();

		ThreadA a = new ThreadA(service);
		a.setName("A");
		a.start();

		ThreadB b = new ThreadB(service);
		b.setName("B");
		b.start();

		Thread.sleep(3000);

		service.signalAll();

	}
}
/*
result:
begin awaitA時間為1540885187756 ThreadName=A
begin awaitB時間為1540885187756 ThreadName=B
  signalAll時間為1540885190756 ThreadName=main
  end awaitA時間為1540885190756 ThreadName=A
  end awaitB時間為1540885190756 ThreadName=B
*/

如果先要對執行緒分組進行喚醒,那麼就可以使用多個Condition物件,每一個物件都與一組
執行緒繫結。
正確用法舉例:

package chapter04.section01.thread_4_1_6.project_1_MustUseMoreCondition_Ok;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	public Condition conditionA = lock.newCondition();
	public Condition conditionB = lock.newCondition();
	
	public void awaitA() {
		try {
			lock.lock();
			System.out.println("begin awaitA時間為" + System.currentTimeMillis()
			+ " ThreadName=" + Thread.currentThread().getName());
			conditionA.await();
			System.out.println("  end awaitA時間為" + System.currentTimeMillis() 
			+ " ThreadName=" + Thread.currentThread().getName());
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void awaitB() {
		try {
			lock.lock();
			System.out.println("begin awaitB時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
			conditionB.await();
			System.out.println("  end awaitB時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void signalAll_A() {
		try {
			lock.lock();
			System.out.println("  signalAll_A時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
			conditionA.signalAll();
		} finally {
			lock.unlock();
		}
	}

	public void signalAll_B() {
		try {
			lock.lock();
			System.out.println("  signalAll_B時間為" + System.currentTimeMillis()
					+ " ThreadName=" + Thread.currentThread().getName());
			conditionB.signalAll();
		} finally {
			lock.unlock();
		}
	}
}


package chapter04.section01.thread_4_1_6.project_1_MustUseMoreCondition_Ok;

public class Run {

	public static void main(String[] args) throws InterruptedException {

		MyService service = new MyService();

		ThreadA a = new ThreadA(service);
		a.setName("A");
		a.start();

		ThreadB b = new ThreadB(service);
		b.setName("B");
		b.start();

		Thread.sleep(3000);

		service.signalAll_A();
	}
}
/*
result:
begin awaitA時間為1540885681597 ThreadName=A
begin awaitB時間為1540885681598 ThreadName=B
  signalAll_A時間為1540885684599 ThreadName=main
  end awaitA時間為1540885684600 ThreadName=A
*/

執行緒ThreadA和ThreadB與前面例子一樣
可以看到使用ReentrantLock物件可以喚醒指定種類的執行緒,這是控制部分執行緒行為的方便
方式


(5) 實現生產者和消費者模式
舉例1 一對一

package chapter04.section01.thread_4_1_7.project_1_ConditionTest;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	private boolean hasValue = false;
	
	public void set() {
		try {
			lock.lock();
			while(hasValue == true) {
				condition.await();
			}
			System.out.println("列印★");
			hasValue = true;
			condition.signal();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void get() {
		try {
			lock.lock();
			while(hasValue == false) {
				condition.await();
			}
			System.out.println("列印☆");
			hasValue = false;
			condition.signal();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
}


package chapter04.section01.thread_4_1_7.project_1_ConditionTest;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		while(true) {
			service.set();
		}
	}
}


package chapter04.section01.thread_4_1_7.project_1_ConditionTest;

public class ThreadB extends Thread {

	private MyService service;

	public ThreadB(MyService service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		while(true) {
			service.get();
		}
	}
}


package chapter04.section01.thread_4_1_7.project_1_ConditionTest;

import chapter02.section01.thread_2_1_8.project_1_synNotExtends.MyThreadA;

public class Run {

	public static void main(String[] args) throws InterruptedException {
		MyService myService = new MyService();
		
		ThreadA a = new ThreadA(myService);
		a.start();
		
		ThreadB b = new ThreadB(myService);
		b.start();
	}
}
/*
列印☆
列印★
列印☆
列印★
列印☆
列印★
列印☆
列印★
列印☆
列印★
*/

 

多對多交替列印:

package chapter04.section01.thread_4_1_8.project_1_ConditionTestManyToMany;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {
	private Lock lock = new ReentrantLock();
	private Condition condition = lock.newCondition();
	private boolean hasValue = false;
	
	public void set() {
		try {
			lock.lock();
			while(hasValue == true) {
				System.out.println("有可能★★連續");
				condition.await();
			}
			System.out.println("列印★");
			hasValue = true;
			//condition.signal();
			condition.signalAll();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public void get() {
		try {
			lock.lock();
			while(hasValue == false) {
				System.out.println("有可能☆連續");
				condition.await();
			}
			System.out.println("列印☆");
			hasValue = false;
			//condition.signal();
			condition.signalAll();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
}


package chapter04.section01.thread_4_1_8.project_1_ConditionTestManyToMany;

public class ThreadA extends Thread{
	private MyService service;
	
	public ThreadA(MyService service) {
		super();
		this.service = service;
	}
	
	@Override
	public void run() {
		while(true) {
			service.set();
		}
	}
}


package chapter04.section01.thread_4_1_8.project_1_ConditionTestManyToMany;

public class ThreadB extends Thread {

	private MyService service;

	public ThreadB(MyService service) {
		super();
		this.service = service;
	}

	@Override
	public void run() {
		while(true) {
			service.get();
		}
	}
}


package chapter04.section01.thread_4_1_8.project_1_ConditionTestManyToMany;

import chapter02.section01.thread_2_1_8.project_1_synNotExtends.MyThreadA;

public class Run {

	public static void main(String[] args) throws InterruptedException {
		MyService myService = new MyService();
		
		ThreadA[] threadA = new ThreadA[10];
		ThreadB[] threadB = new ThreadB[10];
		
		for(int i = 0; i < 10; i++) {
			threadA[i] = new ThreadA(myService);
			threadB[i] = new ThreadB(myService);
			threadA[i].start();
			threadB[i].start();
		}
	}
}
/*
有可能★★連續
有可能★★連續
有可能★★連續
有可能★★連續
列印☆
有可能☆連續
有可能☆連續
有可能☆連續
有可能☆連續
有可能☆連續
列印★
有可能★★連續
有可能★★連續
有可能★★連續
列印☆
有可能☆連續
有可能☆連續
*/