EP35 线程协作

135 阅读2分钟

昨天说的线程同步的场景是,多个线程想要同时操作一个对象,容易发生错乱;

现在有一种新的场景,两个线程要共享某个数据对象,跟前面不同的是,这里线程A负责给这个对象增加数据,线程B负责给这个对象减少数据。比如定义一个ArrayList对象listList <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,还需要waitnotify。注意waitnotify必须在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]