在jdk1.6之前,Synchronized是基于重量级锁实现的,就是说,当多个线程竞争同一把锁的时候,
如果获取不到锁,线程就会阻塞,等待锁的释放,直到获取锁。
Synchronized是jvm层面的锁。无锁->偏向锁->轻量级锁->重量级锁的升级是jvm实现的。锁升级后是不能降级的。
偏向锁,轻量级锁 是无锁化实现的,是乐观锁。重量级锁是悲观锁。
偏向锁是通过cas方式实现的。
轻量级锁是采用自旋(为什么用自旋,而不用挂起?因为大部分情况下,某个线程获取锁以后会很快释放锁,这个时候如果让线程挂起会消耗性能,所以使用自旋的方式获取锁性能更好。但是自旋后浪费cpu自旋,所以在自旋一定次数以后如果还没有获取到锁,就会进行挂起,膨胀为重量级锁。自旋次数有自己设置和自适应,通过preBlockSpin参数进行设置)的方式实现的。
之后,Synchronized优化为偏向锁,轻量级锁,重量级锁按条件处理的方式。
假如有2个线程ThreadA、和ThreadB:
1.大部分情况下,只有ThreadA去访问,这个时候对象头存放的是轻量级锁标记,记录的是ThreadA线程的线程id以及偏向锁标记
2.当ThreadA和ThreadB交替访问的时候,会转化为轻量级锁,通过自旋的方式获取锁。
3.当多个线程同时访问的时候,由于通过轻量级锁也无法获取锁,就会转化为重量级锁,对获取不到锁的线程进行阻塞挂起。
package com.example;
public class ThreadA extends Thread {
Object lock ;
public ThreadA(Object lock) {
this.lock = lock;
}
@Override
public void run() {
System.out.println("start thread A");
synchronized (lock){
try {
lock.wait();//阻塞并且释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end thread A");
}
}
package com.example;
public class ThreadB extends Thread {
Object lock ;
public ThreadB(Object lock) {
this.lock = lock;
}
@Override
public void run() {
System.out.println("start thread B");
synchronized (lock){
try {
lock.notify();//唤醒其他阻塞的线程
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("end thread B");
}
}
package com.example;
public class WaitNotifyTest {
public static void main(String[] args) {
Object lock = new Object();
//wait 1.实现线程的阻塞,2.释放当前的同步锁
ThreadA threadA = new ThreadA(lock);
threadA.start();
//notify 1.唤醒被阻塞的线程
ThreadB threadB = new ThreadB(lock);
threadB.start();
}
}
输出结果:
start thread A 线程a启动,获得同步锁,启动后wait() ,线程阻塞,释放锁 ,a会被放到线程“等待队列”中。
start thread B 线程b启动,唤醒其他阻塞的线程a,a会被放到"同步队列中",等待争取锁,此时锁掌握在b手中,a无法获取锁,只能等b释放锁以后a才能获取锁执行。线程b只能唤醒等待同一把锁的线:a等待lock锁
end thread B 线程b执行结束,释放锁。
end thread A 线程a获取锁,继续执行。
线程wait后进入等待队列,等待被唤醒,其他线程notify唤醒后进入同步队列,等待争抢锁