文章目录
一、synchronized的基本知识
首先要明白,synchronized关键字锁的是对象,而不是代码块,每个对象的对象头中的markword中有两个二进制位用于标记对象的锁状态,每个对象还有一个监视器Monitor用于实现锁。
监视器Monitor的实现及原理
二、synchronized的使用
2.1、修饰普通方法
/**
* synchronized使用方式1:修饰普通方法
* 锁的是当前对象 this
*/
public synchronized void way1() {
System.out.println("synchronized使用方式1:修饰普通方法");
}
2.2、修饰静态方法
/**
* synchronized使用方式2:修饰静态方法
* 锁的是类的Class对象
*/
public static synchronized void way3() {
System.out.println("synchronized使用方式2:修饰静态方法");
}
2.3、修饰代码块
/**
* 创建一个对象,用于加锁
* 对象相当于一把锁
*/
private final Object o = new Object();
/**
* synchronized使用方式3:修饰代码块
* 传入对象是this的话,锁的是当前对象
* 传入对象o的话,锁的是对象o
* 传入HowToUseSynchronized.class对象的话,锁的是类的Class对象
*/
public synchronized void way2() {
// 传入对象是this的话,锁的是当前对象
// 传入对象o的话,锁的是对象o
// 传入HowToUseSynchronized.class对象的话,锁的是Class对象
// synchronized (this) {
// synchronized (HowToUseSynchronized.class) {
synchronized (o) {
System.out.println("synchronized使用方式3:修饰代码块");
}
}

三、synchronized的锁升级
JDK1.6之后,为了减少获得锁和释放锁带来的性能消耗,synchronized引入了锁升级,并不是一开始就使用操作系统级别的重量级锁。
3.1、无锁
一个对象锁没有线程获取过时,处于无锁状态。
3.2、偏向锁
一个对象锁第一次有线程获取时,对象头中的MarkWord会记录线程的ID(记为线程1),此时处于偏向锁状态。
当有线程(记为线程2)再次尝试获取该对象锁时,先会判断是否为MarkWord中记录的线程ID,若是,线程2可直接获得对象锁,执行同步代码块;若不是,则需查看线程1当前是否存活,若线程1已运行结束,则将MarkWord中的线程ID设置为线程2,若线程1还存活,那么查看线程1的栈帧信息,如果线程2不需要继续持有该对象锁,那么将MarkWord中的线程ID设置为线程2,如果线程1需要继续持有该对象锁,则升级为轻量级锁(自旋锁)。
3.3、轻量级锁 - 自旋锁CAS
自旋锁,即未获取到对象锁的线程循环尝试获取锁(默认尝试10次),如果在这循环尝试期间,持有对象锁的线程刚好释放了锁,那么正在自旋获取锁的线程就有机会获取到对象锁。如果循环尝试没有成功,那么会升级为重量级锁。
3.4、重量级锁
操作系统级别的锁,如lock指令等。
四、synchronized的使用注意事项
- synchronized锁定的对象不能是String常量。当两个毫不相干的同步代码块都需要获取字符串“1234”常量才能允许,由于字符“1234”在常量池中只有一份,那么这两个毫不相干的同步代码块会相互影响,甚至产生死锁
- synchronized锁定的对象也不能是Integer、Long等基本数据类型的包装类,虚拟机缓存-128~127的整型包装类对象,所有在-128 ~ 127之间的整型包装类对象是同一个对象
918

被折叠的 条评论
为什么被折叠?



