j.u.c.locks.condition

分享JUC中的Condition的比較少見,我見了大部分文章都是講其中的一個例子BoundedBuffer。今天先從Condition接口的幾個方法提及,而後在把BoundedBuffer搞死鎖了。來看看Condition在使用的時候須要注意什麼。java

源代碼

上源代碼:(字數限時,原諒我把註釋都去掉了)數組

public interface Condition {
    
    void await() throws InterruptedException;
    
    void awaitUninterruptibly();
    
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    void signal();
    
    void signalAll();
}

Condtion接口主要是用來描述一個鎖的幾個狀態。具體建立方法以下
ide

Condition notFull = lock.newCondition();

其實Condition的功能有點相似Object當中的wait和notify方法。可是稍作了增強。提供了多種wait的策略。另外用Condition的最大好處就是,一個鎖是能夠擁有多個狀態的。若是用Object的wait和notify只能有一個。
測試

具體看一下如下幾個wait方法:
this

    void await() throws InterruptedException;
    
    void awaitUninterruptibly();
    
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    boolean awaitUntil(Date deadline) throws InterruptedException;

await()spa

解讀:會將當前線程設置成等待狀態,一直到有signal方法的觸發或者Interrupt的發生纔會恢復狀態。固然interrupt了就直接拋異常了,不會繼續往下走線程

異常:InterruptedExceptioncode

awaitUninterruptibly()接口

解讀:會將當前線程設置成等待狀態,一直到有signal方法的觸發。不會被Interrupt阻斷。get

long awaitNanos(long nanosTimeout)

參數:最大的等待的時間

返回:實際的剩餘時間的估算(nanosTimeout - 實際等待時間)正值能夠用於後續的繼續等待,例如延遲加載這樣的場景,可讓程序繼續等待剩下的時間,完成計時。若是爲0或者負數時,表示沒有剩餘時間了。

解讀:會將當前線程設置成等待狀態一直到設定的最大等待時間。當遇到singal或者Interrupt時纔會恢復。

異常:InterruptedException

boolean await(long time, TimeUnit unit) 和 boolean awaitUntil(Date deadline)

參數:具體時間一個是間斷的時間,另外一個是具體的時刻。但實質是同樣的。

解讀:具體等待一段時間或一個到一個時間點。若是遇到singal則返回True,不然返回false.

異常:InterruptedException

    void signal();
    
    void signalAll();

void signal()

解讀:喚醒一個等待的線程,若是全部的線程都在等待此條件,則選擇其中的一個喚醒。在從 await 返回以前,該線程必須從新獲取鎖。

void signalAll()

解讀:喚醒全部線程,若是全部的線程都在等待此條件,則選擇其中的一個喚醒。在從 await 返回以前,該線程必須從新獲取鎖。


看一個例子

public class BoundedBuffer {
	final Lock lock = new ReentrantLock();
	final Condition notFull = lock.newCondition();
	final Condition notEmpty = lock.newCondition();
	
	final Object[] items = new Object[2];
	int putptr, takeptr, count;
	
	public void put(Object x) throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println(threadName+" is waiting for lock");
		lock.lock();
		System.out.println(threadName+" got lock");
		try {
			while (count == items.length){
				System.out.println(threadName+" is waiting for notFull condition");
				notFull.await();
				System.out.println(threadName+" left notFull condition");
			}
			items[putptr] = x;
			if (++putptr == items.length){
				putptr = 0;
			}
			++count;
			notEmpty.signal();
		} finally {
			lock.unlock();
		}
	}
	
	public Object take() throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println(threadName+" is waiting for lock");
		lock.lock();
		System.out.println(threadName+" got lock");
		try {
			while (count == 0){
				System.out.println(threadName+" is waiting for notEmpty condition");
				notEmpty.await();
				System.out.println(threadName+" left notEmpty condition");
			}
			Object x = items[takeptr];
			if (++takeptr == items.length){
				takeptr = 0;
			}
			--count;
			notFull.signal();
			return x;
		} finally {
			lock.unlock();
		}
	}
}

問得最多的一個問題:這裏的await有必要嗎?

while (count == 0){
    System.out.println(threadName+" is waiting for notEmpty condition");
    notEmpty.await();
    System.out.println(threadName+" left notEmpty condition");
}

回答:有必要,當count等於0的時候.若是沒有await釋放鎖,那麼其餘線程會認爲lock一直被佔有,將沒法得到鎖。


爲了驗證這一點,寫了一個測試方法:

public class PutTask implements Runnable {
	BoundedBuffer bb = new BoundedBuffer();
	int count = 0;
	
	public PutTask(BoundedBuffer bb){
		this.bb = bb;
		this.count = 0;
	}
	
	@Override
	public void run() {
		try {
			String threadName = Thread.currentThread().getName();
			bb.put(count);
			System.out.println(threadName+" put "+count);
			count++;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class TakeTask implements Runnable {
	BoundedBuffer bb = new BoundedBuffer();
	
	public TakeTask(BoundedBuffer bb){
		this.bb = bb;
	}
	
	@Override
	public void run() {
		try {
			System.out.println(Thread.currentThread().getName()+" take "+bb.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class ConditionTest {
	
	public static void main(String[] args) throws InterruptedException{
		BoundedBuffer bb = new BoundedBuffer();
		
		PutTask putTask = new PutTask(bb);
		TakeTask takeTask1 = new TakeTask(bb);
		TakeTask takeTask2 = new TakeTask(bb);
		
		final ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
		//service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS);
		Thread.currentThread().sleep(3000l);
		Thread t = new Thread(putTask);
		t.start();
	}
}

將notEmpty.await()註釋掉,執行的結果爲

pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-1 is waiting for lock
Thread-0 is waiting for lock

能夠看到除了pool-1-thread-2得到了鎖,其餘線程都在等待鎖。但這個時候pool-1-thread-2被while(count==0)鎖死,沒法跳出。程序進入死鎖。


恢復notEmpty.await()的註釋,執行的結果爲

pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
Thread-0 is waiting for lock
Thread-0 got lock
Thread-0 put 0
pool-1-thread-2 left notEmpty condition
pool-1-thread-2 take 0
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition



問題:

while (count == 0){
    System.out.println(threadName+" is waiting for notEmpty condition");
    notEmpty.await();
    System.out.println(threadName+" left notEmpty condition");
}

若是將while改爲if能夠嗎?

回答:不能夠,

notEmpty.signal();

若是將notEmpty.signal()改爲notEmpty.signalAll()。而後按照上面的方法再來試一次,先放兩個線程去take,而後一個線程去put,而後同時喚醒兩個線程。數組會越界。必須從新等待一次notEmpty.


最後一個問題

public class ConditionTest {
	
	public static void main(String[] args) throws InterruptedException{
		BoundedBuffer bb = new BoundedBuffer();
		
		PutTask putTask = new PutTask(bb);
		TakeTask takeTask1 = new TakeTask(bb);
		TakeTask takeTask2 = new TakeTask(bb);
		
		final ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
		service.scheduleAtFixedRate(putTask, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask1, 0, 1, TimeUnit.SECONDS);
		service.scheduleAtFixedRate(takeTask2, 0, 1, TimeUnit.SECONDS);
	}
}

若是這樣執行,結果會怎麼樣?在兩個線程中跑3個任務。

結果就是死鎖了:

pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 put 0
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 take 0
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 put 1
pool-1-thread-1 left notEmpty condition
pool-1-thread-1 take 1
pool-1-thread-1 is waiting for lock
pool-1-thread-1 got lock
pool-1-thread-1 is waiting for notEmpty condition
pool-1-thread-2 is waiting for lock
pool-1-thread-2 got lock
pool-1-thread-2 is waiting for notEmpty condition
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息