一、synchronized和锁的一些细节
1、synchronized 不能被继承,当父类方法使用synchronized修饰方法时,子类继承的方法不具有synchronized作用。
2、构造方法不可能真的同步,尽管synchronized可以修饰构造方法。
3、所有的锁都是块结构的。在进入synchronized 修饰的同步方法或者同步代码块之前线程需要获取当前对象的锁,退出(即使是异常退出)必须正确释放锁。
4、锁的操作是建立在独立的线程之上,而不是独立调用的基础之上。同一个线程如果已经持有了一个锁对象,那么被这个锁对象加锁的所有代码块或者方法该线程都是能够调用,而不会被阻塞的。(synchronized可重入的原理)
5、线程遵循获得锁,释放锁的一个前提是锁的对象是同一个,即如果锁的对象不是同一个的两个不同方法或者代码块,可以同时被两个线程操作,假设这个两个方法里面都有对变量A的操作,那么这个加锁是没有保证线程安全的。所以同步不等于原子性,但是同步机制可以实现原子性。
6、线程获取锁没有任何公平性保证。A线程先获得锁,当A释放锁的时候,B线程已经在等待,此时线程A和线程B谁获得锁,将不能被保证。
二、监视器
1、每个对象都有一个锁,同时每个对象也有一个线程管理的集合。这些集合由jvm管理,并可以通过(wait、notify、notifyAll、Thread、interrupt)管理。拥有锁和等待集合的实体通常被称为监视器。任何一个对象都是一个监视器。
2、监视器对其持有的集合的操作的方法介绍:
- Wait
1> 如果线程已经终止,那么会直接返回并抛出InterruptedException异常。否则当前线程 进入阻塞状态
2> java虚拟机将该线程放置在目标对象的等待集合中。
3> 释放目标对象的锁,即使是在目标对象上多次嵌套的同步调用,所持有的可重入锁也会完整的释放(其他对象的锁并不释放,处理不好,造成死锁的原因之一)。
- Notify
1> java 虚拟机从等待该对象锁的线程集合中选择一个线程A,(前提:等待该对象锁的线程总数大于等于1),并将线程A从等待集合中移除。当等待集合中存在多个等待线程的时候,并不能保证哪个线程优先被移除(即不保证公平性)。
2>移除的线程A将继续与其他新的线程(非等待该锁的线程)竞争锁。如果竞争失败,将继续进入集合等待,竞争成功则继续从wait之后的代码继续执行。
- NotifyAll
1> 与Notify机制一样,只是NotifyAll是从等待该锁的所有线程移除集合参与锁的竞争,只有竞争成功的那一个线程不会被重新放入集合等待。
- Interrupt
1>interrupt 在阻塞的线程上调用,和Notify有一样的效果,只是当中断之后的线程获得锁之后,会抛出一个IntegrruptException,然后再清除中断标志。当interrupt和notify同时调用时,没办法保证那一个先执行。
- sleep(0)、wait(0)
理论上可以让线程重新竞争CPU资源。