1. Java中線程協作的最常見的兩種方式:利用Object.wait()、Object.notify()和使用Condition
(1)複習一下Object.wait()、Object.notify()
在java中,對於任意一個java對象,它都擁有一組定義在java.lang.Object上監視器方法,包括wait(),wait(long timeout),notify(),notifyAll(),這些方法配合synchronized關鍵字一起使用可以實現等待/通知模式。
以前的文章:https://blog.csdn.net/zhm1563550235/article/details/84259542
(2)什麼是Condition接口?
Condition接口也提供了類似Object監視器的方法,通過與Lock配合來實現等待/通知模式。
2. Condition接口
接口Condition把Object的wait和notification方法(包括wait()、notify()、notifyAll())分解到不同的條件對象中。通過把這些條件和任意Lock實現的使用組合起來,起到讓每個對象上具有多重等待集合的作用。這裏Lock取代了同步方法、同步代碼塊,condition取代了Object的wait、notification方法。
3. Condition聲明的方法
方法名稱 | 描述 |
void await() | 在接收到信號或被中斷以前,強制調用線程一直等待。 |
boolean await(long time, TimeUnit unit) | 在接收到信號、被中斷,或者超過指定的等待時間之前,強制調用線程一直等待。
同樣是在await()基礎上增加了超時響應,不同的是:
|
long awaitNanos(long nanosTimeout) |
在接收到信號,被中斷,或者超過指定的等待時間之前,強制調用線程一直等待。同樣是在await()基礎上增加了超時響應,不同的是: 返回值表示當前剩餘的時間,如果在nanosTimeout之前被喚醒,返回值 = nanosTimeout - 實際消耗的時間,返回值 <= 0表示超時; |
void awaitUninterruptibly() | 在接收到信號之前,強制當前線程一直等待。 |
boolean awaitUntil(Date deadline) | 在接收到信號、被中斷,或者超過指定的截止時間之前,強制當前的線程一直等待。 |
void signal() | 喚醒一個等待中的線程。 |
void signalAll() | 喚醒所有等待中的線程。 |
4. 獲取Condition
Condition實例實質上被綁定到一個鎖上。一個鎖內部可以有多個Condition,即有多路等待和通知。要爲特定 Lock 實例獲得 Condition 實例,請使用其 newCondition() 方法。
Conditon newCondition() 返回用來與當前Lock實例一起使用的Condition實例。
類似於 object.wait()和object.notify()的功能。object.wait()與object.notify()需要結合synchronized使用。Condition需要結合ReentrantLock使用。
5. Condition與循環一起使用,防止"虛假喚醒"
在等待Condition時,允許發生"虛假喚醒",這通常作爲對基礎平臺語義的讓步。若使用"if(!條件)"則被"虛假喚醒"的線程可能繼續執行。所以"while(!條件)"可以防止"虛假喚醒"。建議總是假定這些"虛假喚醒"可能發生,因此總是在一個循環中等待。
6. Condition的使用
(1)使用Object的wait()和notify()實現生產者消費者:
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
synchronized (queue) {
while(queue.size() == 0){
try {
System.out.println("隊列空,等待數據");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.poll(); //每次移走隊首元素
queue.notify();
System.out.println("從隊列取走一個元素,隊列剩餘"+queue.size()+"個元素");
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
synchronized (queue) {
while(queue.size() == queueSize){
try {
System.out.println("隊列滿,等待有空餘空間");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(1); //每次插入一個元素
queue.notify();
System.out.println("向隊列取中插入一個元素,隊列剩餘空間:"+(queueSize-queue.size()));
}
}
}
}
}
(2)使用Condition實現消費者生產者
public class Test {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
Test test = new Test();
Producer producer = test.new Producer();
Consumer consumer = test.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run() {
consume();
}
private void consume() {
while(true){
lock.lock();
try {
while(queue.size() == 0){
try {
System.out.println("隊列空,等待數據");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll(); //每次移走隊首元素
notFull.signal();
System.out.println("從隊列取走一個元素,隊列剩餘"+queue.size()+"個元素");
} finally{
lock.unlock();
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
produce();
}
private void produce() {
while(true){
lock.lock();
try {
while(queue.size() == queueSize){
try {
System.out.println("隊列滿,等待有空餘空間");
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1); //每次插入一個元素
notEmpty.signal();
System.out.println("向隊列取中插入一個元素,隊列剩餘空間:"+(queueSize-queue.size()));
} finally{
lock.unlock();
}
}
}
}
}