关于synchronized的几点说明

释放同步监视器的时机


任何线程进入同步代码块,同步方法之前,必须先获得对同步监视器的锁定。由于程序无法显式释放对同步监视器的锁定,那么究竟何时会释放同步监视器锁呢?
  • 当前线程的同步代码块或同步方法正常执行结束;
  • 当前线程在同步代码块或同步方法中遇到break或return语句返回;
  • 当前线程在执行同步代码块或同步方法时发生了未处理的Error或Exception,导致程序异常结束;
  • 当前线程在同步代码块或同步方法中调用wait()方法。
在如下所示的情况下,线程不会释放同步监视器锁:
  • 线程在同步代码块或同步方法中调用Thread.sleep()或Thread.yield()方法来暂停当前线程的执行;
  • 线程在同步代码块或同步方法中调用suspend() 方法挂起当前线程(当然,我们应该尽量避免使用suspend()和resume()方法来控制线程)。

wait()


Object类中有5个方法组成了等待/通知机制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的类都从Object类t继承而来,因此所有的类都拥有这些公有方法可供使用。而且,由于它们都被声明为final,因此在子类中不能覆盖它们中的任何一个方法。
<span style="font-size:14px;">public final void wait() throws InterruptedException, IllegalMonitorStateException</span>
该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。调用wait()方法后当前线程会释放锁。在从wait()返回前,当前线程与其他线程竞争重新获得锁。如果调用wait()方法时,没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此不需要try-catch结构。

notify()


public final native void notify() throws IllegalMonitorStateException
该方法也要在同步方法或同步块中调用,如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。
notify()方法用来通知那些可能等待该对象级别锁的其他线程。如果有多个线程在等待同一把锁,则线程规划器会任意挑选出其中一个wait()状态的线程来发出通知,并使它等待获取该对象的对象级别锁。注意,调用notify()方法后,当前线程不会马上释放该对象级别锁,必须要等到线程退出同步代码块或同步方法后才会释放对象级别锁,wait状态的线程才可以获取该对象锁。
当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次调用notify()方法,则即便同步锁已经空闲,其他wait状态的线程也不会受到该对象的通知,会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll通知。这里需要注意的是,它们等待的是被notify或notifyAll,而不是锁,这与下面的notifyAll()方法执行后的情况不同。

notifyAll()


public final native void notifyAll() throws IllegalMonitorStateException
该方法与notify()方法的工作方式基本相同,不同的地方在于notifyAll()会使所有原来在该对象上wait的线程统统退出wait的状态(即全部被唤醒,不再等待notify或notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的对象级别锁,一旦该对象锁被释放,他们就会立即去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,其他线程继续等待。在该线程退出同步代码块或同步方法释放锁后,其他的已经被唤醒的线程将会继续竞争获取该对象锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

wait(long)和wait(long, int)


显然,这两个方法是设置等待超时时间的,后者在超值时间上加上ns,精度也难以达到,因此该方法很少使用。对于前者,如果线程在等待获取对象锁的过程中已经超过了指定的毫秒数,则它通过竞争重新获得锁,并从wait(long)返回。另外,需要知道,如果设置了超时时间,当wait返回时,我们不能确定它是因为接到了通知还是因为超时而返回的,因为wait方法不会返回任何相关的信息。但一般可以通过设置标志位来判断,在notify之前改变标志位的值,在wait方法后读取该标志位的值来判断,当然为了保证notify不被遗漏,我们还需要另外一个标志位来循环判断是否调用wait方法。


发布了31 篇原创文章 · 获赞 19 · 访问量 10万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章