Java极致并发:(二)synchronized、wait、notify

Apr 19, 2016   #并发  #Java 
Java中的所有对象通过synchronized关键字都可以当作锁(intrinsic lock or monitor lock)来使用(个人觉得没有必要,且每个对象都必须为此付出内存开销)。

线程在执行synchronized方法或语句块之前,会去获得该对象的lock,方法或语句块执行完成之后,会释放对应的lock。

synchronized关键字提供了一种非常简单的线程互斥工具,其内部通过jvm的monitorenter,monitorexit指令来实现。

上一篇也讲到了,除了提供互斥语义之外,JMM还规定了获得monitor的线程,对上一个monitor的拥有者在monitor范围内执行的操作是可见的。

因此,下面的代码变量i并不需要添加修饰符volatile。

public class Example_1 {
    private int 0;

    void synchronized increment() {
        i++;
    }
}

但这样的好处并不是免费得来的,JSR 133 FAQ中提到

After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads. Before we can enter a synchronized block, we acquire the monitor, which has the effect of invalidating the local processor cache so that variables will be reloaded from main memory. We will then be able to see all of the writes made visible by the previous release.

这也意味着,synchronized同步操作的开销是比较大的。。

synchronized仅提供了线程之间的互斥操作,并不能完成线程间的协作。考虑生产者和消费者问题,当消费者没有物品可以消费时,只能不断轮询,消费cpu时钟周期。

Java中的每个对象都提供了wait和notify两种操作,这两种操作都必须在synchronized块中才能发挥作用(加锁的对象和执行wait、notify的对象必须是同一个)。

wait操作会导致当前线程放弃该对象的monitor,并阻塞,阻塞的线程会加入该对象的阻塞列表。

此时,其他线程有机会获得该对象的monitor,若其对该对象执行notify操作,会从该对象的阻塞列表中找出一个线程唤醒。注意被唤醒的线程不会立即运行,而需要当前线程离开monitor之后。

简而言之,wait & notify提供了一种线程间协作的方式。一个无法取得进展的线程放弃索锁并阻塞,将锁让给其他能够取得进展的线程。当其他线程取得进展后,再通知阻塞的线程可以继续运行。


下一篇将介绍JDK 1.5提供了Lock类提供的锁功能,与synchronized这种JVM内置的锁支持不同,Lock类是由Java语言层面上实现的。