我们在编写程序时,总会看到每个对象都有几个相同的方法,比如说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进阶驿站