实现
- 非静态方法的代码块
按照Hotspot的实现来看,是在这个对象的对象头位置(64位,前两位 ##mark word)记录了这个对象锁的状态。
public synchronized void m1() {
System.out.println(Thread.currentThread().getName() + " m1 start...");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m1 end");
}
- 静态方法的代码块
这个类T在被类加载器加载到jvm内存中的时候,生成了一个特殊的类Class<T>。静态方法的代码块锁其实就是synchronized(T.class)
public synchronized static void m() { //这里等同于synchronized(T.class)
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
引申问题:T.class 加载到内存中一定是单例么?
并不一定是。如果使用的同一个ClassLoader那一定是,不同的ClassLoader不一定。
- 对象锁
任何对象都需要先拿到O这个对象的锁
public void m() {
synchronized(o) { //任何线程要执行下面的代码,必须先拿到o的锁
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
- 可重入锁
同一个线程中,允许对同一个对象锁多次。---必须是可重入锁
synchronized void m1() {
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2");
}
synchronize的锁升级
《没错,我就是厕所所长》
https://www.jianshu.com/p/b43b7bf5e052
https://www.jianshu.com/p/16c8b3707436
偏向锁
当一个线程来了之后,只是在MarkWord上面标记了状态(线程的id值),其实并没有真正的加锁。下次还是这个线程访问的时候,发现线程id是一样的,那么就直接使用,不需要加锁。
自旋锁
在一个线程持有偏向锁的时候,另一个线程也要来申请使用这个锁,那么就锁升级进入了自旋锁。
自旋锁类似于一直while(true)循环,一直在查看锁状态,是在内核态进行的。###自旋锁速度快。
对应的线程不停的在CPU上运行,假设1W个线程争抢一把锁,有一个1个获得了锁,剩下的9999个都在那里自旋,所以CPU压力是非常大的。 ###自旋锁会占用CPU
所以自旋锁适合-->线程数少,并且不会长时间运行的线程
重量级锁(自旋10次之后,升级为重量级锁)
重量级锁是进入os,进入等待队列,这个时候线程不再占CPU,进入了等待队列。由CPU决定是什么时候运行。
所以重量级锁适合-->线程数量多,并且会长时间运行的线程