java中,synchronized永远都是锁定的一个对象,那么jvm是怎么判断一个对象是被锁定的呢。java
Java的对象由对象头,对象体和填充空间(Padding)组成。数组
下图为32bit的JVM虚拟机中,Mark Word的组成:
多线程
MarkWord经过标志位来记录对象当前的锁信息。不一样标示位的状况下,Mark Word记录的数据不同。jvm
无锁状态下,对象不具有排他性,此时全部线程均可以直接访问这个对象。性能
偏向锁实际上就是无锁,此时标记字记录的是当前操做这个对象的线程ID。线程
此时对象被锁定,标记字指向持有当前对象的线程的地址。等待的线程会进入自旋状态,经过CAS来争抢锁。指针
此时对象被锁定,标记字指向当前对象的Monitor(对象监视器)的地址。等待的线程会进入阻塞状态。对象
Java的锁只会升级,不会降级。当锁所有被释放后,会回到初始状态,等待再次被升级。内存
JDK6开始,JVM默认打开了偏向锁,所以默认状况下,一个对象被建立时,对象头中是偏向锁的信息。虚拟机
此时JVM默认永远只有这个线程使用这个对象,为了减小性能消耗,会进入偏向锁状态,实际上并不会上锁。
当出现了锁争夺时,会升级为轻量级锁,此时两个线程会尝试修改标记字本身的线程地址,修改为功的线程获取到锁,修改失败的线程进入到自旋状态,经过CAS操做来重试修改标记字。
此时JVM会把对象的锁升级为重量级锁,标记字会指向对象的对象监视器(Monitor),全部争夺失败的线程进入阻塞状态。
每一个对象都有一个对应的对象监视器,用来控制对象的多线程访问。
多个线程竞争对象的锁,争夺成功的线程引用放入到Owner中,其余线程放入到锁池中
执行完成后,调用monitorExit,释放锁。
此时从锁池中按照先进先出原则,取出下一个线程,和新进的线程(若是有的话)来争夺锁。
调用wait()方法,线程进入挂起状态,线程的引用会被放入到waitSet中,从锁池中按照先进先出原则,取出下一个线程,和新进的线程(若是有的话)来争夺锁。
调用notify()方法后,从waitSet中拿到一个线程(具体拿哪一个取决于JVM的配置),当即去争夺对象的锁,若是失败则进入到锁池。
调用notify()方法后,从waitSet中拿出全部线程,当即去争夺对象的锁,若是失败则进入到锁池。
除非明确知道只有一个线程出于wait状态,不然就是用notifyAll方法,防止线程等待过久或永远等下去。