昨天说的线程同步的场景是,多个线程想要同时操作一个对象,容易发生错乱;
现在有一种新的场景,两个线程要共享某个数据对象,跟前面不同的是,这里线程A负责给这个对象增加数据,线程B负责给这个对象减少数据。比如定义一个ArrayList
对象list
,List <Object> list = new ArrayList<>();
Thread A负责list.add(object)
,Thread B负责消耗list
里的数据:
Object object = list.get(0);
list.clear();//拿到数据之后清空list
这就是一个「生产者-消费者」形式。
在这个基础上,需求是,仅当list里面没有数据,A才往里面add
;仅当list里面有数据,B才从里面get、clear
。亦即list里面只有0或1个object。也就是说我们希望list里面没有数据的时候,B是阻塞的,A是就绪的;反之A是就绪的,B是阻塞的。这就需要线程之间的协作。
实现这种场景不仅需要synchronized
,还需要wait
和notify
。注意wait
和notify
必须在synchronized
内部用。
get数据的代码在下面;注意里面的wait()
和notify()
,前几集提到过,wait()
是SDK的Object
类的native
方法,描述成:
让当前线程一直等着,直到另一个线程为这个对象执行notify()/notifyAll()方法。跟wait(0)一个效果。
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
public synchronized Object getObject()
{
while (list.size() == 0) {
try {
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
Object object = list.get(0);
list.clear();
notify();// 唤醒阻塞队列的某线程到就绪队列
System.out.println("got it!");
return object;
}
add数据的代码也类似,就是把list.get和下面的clear替换成list.add。
这样一来,两个线程同时执行上面两个不同方法,就不会发生冲突了,永远是add--->get--->add--->get..这样的顺序。
注意到上面检查list里面是否有数据的时候,无论是addObject还是getObject方法都用的是while
而不是if
,这不禁让人疑惑,既然这个wait()是一直阻塞到有人notify为止,为什么还要不停地判断是否需要wait呢?有人给出的解释是:
某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥。
-Jan 10
Reference:
[1]