我們在編寫程序時,總會看到每個對象都有幾個相同的方法,比如說wait()、notify()等,其實它們都是從Object類繼承下來的,用於實現線程同步用的,當然這是jdk1.5之前的方法,jdk1.5之後提供了併發包一系列類用於支持多線程互斥與同步問題。
幾個方法的說明:
- wait()
主動讓出鎖獲得的鎖,一直阻塞直到被喚醒爲止(注意被喚醒的方法有多種),該方法有幾個重載版本,可以指定超時時間。 - notify()
喚醒相應對象的等待集中的一個被任意選中的線程。 - notifyAll()
喚醒相應對象的等待集中的所有線程。
注意這三個方法都必須在相應對象的synchronized代碼塊中被調用,也就是說只有在獲得一個對象的鎖後才能調用這個對象的這三個方法,否則會報IllegalMonitorStateException。
實現生產者-消費者模式(阻塞隊列):
public class ProducerConsumerObject {
private int bufSize = 10;
private int[] buf;
private int currentSize = 0;
private Object obj = new Object();
public ProducerConsumerObject() {
this(10);
}
public ProducerConsumerObject(int bufSize) throws IllegalArgumentException {
this.bufSize = bufSize;
if(bufSize < 0) {
throw new IllegalArgumentException("bufSize can't be less zero");
}
buf = new int[bufSize];
}
public void put(int v) throws InterruptedException{
synchronized (obj) {
while(currentSize >= bufSize) {
obj.wait();
}
buf[currentSize++] = v;
obj.notify();
break;
}
}
public int get() throws InterruptedException{
synchronized (obj) {
while(currentSize == 0) {
obj.wait();
}
int temp = buf[currentSize-1];
buf[currentSize--] = 0;
obj.notifyAll();
return temp;
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
sb.append("queue's length=" + currentSize);
sb.append(" ");
for(int i = 0; i < currentSize - 1; i++) {
sb.append(buf[i] + ",");
}
if(currentSize > 0) {
sb.append(buf[currentSize - 1]);
}
sb.append("]");
return sb.toString();
}
}
對於以上代碼需要說明幾點:
1.put方法和get方法都是阻塞方法,也就是說在put的時候假如隊列已滿,那麼會一直等待直到隊列不滿才返回,在get的時候假如隊列爲空,那麼會一直等待直到隊列不爲空才返回。
2.專門new一個私有Object對象obj作爲鎖,這是因爲我們不希望這裏使用到的鎖在其它地方被使用以讓程序不能正常工作。
3.最好總是把wait放在循環中,因爲如果正在wait可能因爲某種奇怪的原因被“假喚醒”,或者被喚醒後所需條件由被馬上由符合改爲不符合了,這都可能會造成程序崩潰。
4.儘量調用notifyAll而不是notify,特別是在多生產者-多消費者這模型下,否則可能會導致死鎖。
5.notify(或者notifyAll)後需要等到當前線程退出synchronized同步塊wait線程纔有可能搶到鎖。
轉載請註明原文地址:http://blog.csdn.net/u012619640/article/details/48090087
本博客已停止更新,轉移到微信公衆號上寫文章,歡迎關注:Android進階驛站