Java线程锁,synchronized、wait、notify详解

(原)

JAVA多线程这一块有点绕,特别是对于锁,对锁机制理解不清的话,程序出现了问题也很难找到原因,在此记录一下线程的执行以及各种锁。

1、JAVA中,每个对象有且只有一把锁(lock),也叫监视器(monitor)

2、同步(synchronized),synchronized可以修饰的方法或方法中的对象。

3、如果有一个线程进入到了synchronized方法修饰的对象,那么它将会获得这个对象的唯一一把锁,在该线程没有交出这把锁的时候,其它线程是无法访问到该方法中的。该线程会在执行完synchronized方法块中的内容后交出对象锁。

 《Java线程锁,synchronized、wait、notify详解》

 

4、关于wait,notify,属于Object类,并且无法被重写,(网上JDK的1.5和1.6有中文版的API,对于这一块的翻译基本都是机器翻译,很不准确。建议看原版的英文说明文档)。

wait:

4.1、wait方法The current thread must own this object’s monitor,当前线程必需获得这个对象的锁。因为一个线程进入了synchronized的代码块表示这个线程拿到了对象锁,那么这个wait方法必需在synchronized代码块中。

4.2、这个方法让进入到此处的线程丢掉对象锁并且挂起等待(能执行到wait方法的线程一定是拿到了对象锁的线程,如果不理解,请看4.1)。

4.3、其它线程调用这个对象的notify或notifyAll方法时,系统会在当前挂起等待在wait方法处的多个线程中,随机找出一个唤醒,被唤醒的线程会等待直到它拿到了对象锁并继续执行。

4.4、一个线程可能在通知、打断或超时之前被唤醒,这就是所谓的超时欺骗唤醒。实际情况下会很少出现这种情况,应用程序必需防范着判断条件使该线程被唤醒,如果条件不满足需要该线程继续等待。换句话说,wait方法必需放在循环里面。像下面这样。

synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

这句话即使看明白了,也有些难理解,画个图:
4.4.1、比如有线程1进到了synchronzied,拿到了对象锁,此时其它线程都无法进法该synchronized方法块中。

《Java线程锁,synchronized、wait、notify详解》

4.4.2、当线程1执行到wait方法时,会丢掉手上的锁,这时其它线程就能进来了,然后线程1会在此处挂起,不再执行,直到有其它线程调用了这个对象的notify或notifyAll方法。

《Java线程锁,synchronized、wait、notify详解》

4.4.3、此时线程2和线程3以同样的方法来到了wait方法处等待。

《Java线程锁,synchronized、wait、notify详解》

4.4.4、然后又来了一个线程4,进入了该对象的另一个方法,并且调用了该对象的notify方法。

《Java线程锁,synchronized、wait、notify详解》

4.4.5、对象notify被调用后,wait方法处等待的线程中有一个有机会被唤醒,得到锁并继续往下执行。其它线程依旧会在原处等待。

《Java线程锁,synchronized、wait、notify详解》

notify:

Wakes up a single thread that is waiting on this object's monitor 唤醒一个在wait方法处等待的线程。
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. 这个被唤醒的线程不会被执行,直到它得到了这个对象锁。
The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object 被唤醒的线程将会与其它线程去竞争对象锁。(表示被唤醒不代表着马上会执行,除非该线程拿到对象锁。)
Only one thread at a time can own an object's monitor.只有一个线程在这此时得到这个对象锁。

This method should only be called by a thread that is the owner of this object's monitor 这个方法需要被获得了对象锁的线程去调用。
获取对象锁有三种方法:
By executing a synchronized instance method of that object 执行了对象的同步方法。
By executing the body of a {@code synchronized} statement that synchronizes on the object 执行了这个对象同步代码块。
For objects of type {@code Class,} by executing a synchronized static method of that class.对于Class,执行了一个静态的synchronized方法(这表示锁住了class)。

最后解释一下关于下面这段代码,文档中为什么要我们用while,而不是if 或是不加判断之类的
synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

 在上面的例子中,如果这里的条件是

if(num == 1){
  wait();  
}

num++;

notify();

如果三个线程进到wait方法时num为0,

我需要num 为 1时,一直等待,直到它为0才能让num+1

线程2获得了锁,当它执行到notify方法时,num变成了1,此时如果线程1被唤醒得到对象锁后,应该是重新做判断num == 1,如果num为1,还是得继续等待挂起。

如果这里用的是if,那么它表示该线程1在进if 前是满足num ==1这个条件的,但是出if判断时却是不满足num ==1这个条件的。根据已知我需要num 为 1时,一直等待,所以这里的if判断是不对的。

如果这里换成while,那么当线程1被唤醒得到线程锁,它还是得重新做判断,如果num ==  1那么这个线程不将继续等待。

 

    原文作者:冰冻开水
    原文地址: http://www.cnblogs.com/LeeScofiled/p/7225562.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞