synchronized锁
synchronized可以把任意一个非空对象当做锁,它属于独占式的悲观锁,同时又属于可重入锁。
synchronized作用范围
- 作用于方法时,锁住的是对象的实例;
- 作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在元空间,静态方法锁相当于类的一个全局锁,会锁住所有调用该方法的线程;
- 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。
synchronized核心组件
- Wait Set:阻塞队列,那些调用wait方法被阻塞的线程被放置在这里;
- Contention List:竞争队列,所有请求锁的线程首先被放在竞争队列中;
- Entry List:等待队列,竞争队列中那些有资格成为候选资源的线程被移动到等待队列中;
- OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被称为OnDeck;
- Owner:当前已经获取到锁资源的线程。
synchronized实现
- JVM每次从队列的尾部取出一个数据用于锁竞争候选者,但是并发情况下,竞争队列会被大量的并发线程进行CAS访问,为了降低对尾部元素的竞争,JVM会将一部分的线程移动到等待队列中作为候选竞争线程。
- Owner线程会在解锁时,将竞争队列中的部分线程移动到等待队列中,并且指定等待队列中某个线程为OnDeck线程(一般是最先进去的那个线程)。
- Owner线程并不直接把锁传递给OnDeck线程,而是把锁竞争的权利交给OnDeck线程,OnDeck线程需要重新竞争锁。这样虽然牺牲了一些公平性,但是能够极大地提升系统的吞吐量。
- OnDeck线程获取所资源后会变成Owner线程,而没有获取到锁资源的线程仍然停留在等待队列中。如果Owner线程被wait方法阻塞,则转移到阻塞队列中,直到某个时刻通过notify或者是notifyAll唤醒后重新进入等待队列中。
- 处于竞争队列、等待队列、阻塞队列中的线程都处于阻塞状态,该阻塞是通过操作系统来完成的。
- synchronized是非公平锁。在线程进入竞争队列时,等待的线程会先尝试自旋获取锁,如果获取不到就进入竞争队列,这对于已经进入队列的线程是不公平的。
- 每个对象都有monitor对象,加锁就是在竞争monitor对象,代码块加锁是分别在前后添加指令monitorenter和monitorexit来实现的,方法加锁是通过一个标记位来判断的。
- 锁可以从偏向锁升级到轻量级锁,再升级到重量级锁。这种升级过程叫做锁膨胀。