Synchronized进阶
1、锁升级
Java 6之后对 synchronized 进行了改进,引入了锁升级机制,锁会根据线程的竞争情况进行升级。方向为 : **无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁,**可以一定程度减少系统的开销。
无锁:一个线程没有被执行时,就会处于无锁状态。
偏向锁:当只有一个线程执行时,会从无锁升级为偏向锁。
轻量级锁 :当有多个线程交替执行同步块时,会从偏向锁升级为轻量级锁。
重量级锁:当多个线程并发时,会从轻量级锁升级为重量级锁。
2、锁升级过程
2.1、无锁 ==> 偏向锁
当只有一个线程执行时,会从无锁升级为偏向锁。会在对象头中的 Mark Word 中存入该线程的ID。此时,锁的状态为偏向锁。
2.2、偏向锁 ==> 轻量级锁
当有多个线程交替执行同步块时,会从偏向锁升级为轻量级锁。这时在线程的栈中创建锁记录(Lock Record),并通过CAS(Compare and Swap)操作将对象头中的 Mark Word 复制到锁记录中,并将 Mark Word 执行锁记录地址。
2.3、轻量级锁 ==> 重量级锁
当多个线程并发时,会从轻量级锁升级为重量级锁。也就是当多个线程开始竞争锁时,就要从轻量级锁升级为重量级锁。
3、锁降级
锁降级,通常是从高级别转变到低级别的过程。一般会发生在:GC垃圾回收的过程中,JVM 会将索状态重置为无锁状态。
4、Synchronized实现原理
synchronized实现原理主要基于对象头 和Monitor (监视器)机制
4.1、对象头
通常将锁的状态保存在对象头中,对象头主要包含两个部分:Mark Word 和 Klass Pointer
4.1.1、Mark Word
- 长度:32位 JVM 中占32位(4字节),64位 JVM 中占64位(8字节)
- 作用 :主要存储对象的 哈希值、GC分代年龄、锁状态 等运行时的数据。
- 特点:Mark Word 的位布局会根据对象的锁状态动态改变。
4.1.2、Klass Pointer
- 长度:32位 JVM 中占32位(4字节),64位 JVM 中默认开启指针压缩占32位(4字节),不开启是占64位(8字节)。
- 作用:指向对象所属类的元数据,JVM 通过指针确定对象是那个类的实例。
- 特点:堆内存超过32GB时自动关闭指针压缩,恢复为8字节。
4.2、Monitor (监视器)
通常存在于线程栈的锁记录中

5、线程安全的单例模式
java
public class Test02 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
Singleton s3 = Singleton.getInstance();
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
}
}
// 线程安全的单利模式
class Singleton{
// 构造函数的私有化
private Singleton(){}
// 静态的内部类
private static class Holder{
// 单例对象
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return Holder.singleton;
}
}
2003749087
2003749087
2003749087
ReentrantLock可重入锁
1、什么事可重入锁
ReentrantLock是java.util.concurrent.locks 包下的一个类,它提供了比 Synchronized 更灵活、强大的锁机制。"可重入"意味着同一线程可以多次获取同一把锁,而不会被阻塞。
2、核心方法
2.1、锁操作方法
|----------------------------------------------|------------------------|
| 方法签名 | 描述 |
| void lock() | 获取锁,如果锁被其他线程持有,则阻塞当前线程 |
| void lockInterruptibly() | 获取锁,但允许等待时中断 |
| boolean tryLock() | 尝试以非阻塞的方式获取锁 |
| boolean trayLock(long timeout,TimeUnit unit) | 在指定时间内尝试获取锁 |
| void unlock() | 释放锁 |
2.2、状态查询方法
|---------------------------------|--------------|
| 方法签名 | 描述 |
| boolean isHeldByCurrentThread() | 查询当前线程是否持有锁 |
| boolean isLocked() | 查询锁是否被任何线程持有 |
| boolean isFair() | 判断锁是否为公平锁 |
2.3、条件变量方法
|--------------------------|-----------------|
| 方法签名 | 描述 |
| Condition newCondition() | 创建一个与当前锁绑定的条件变量 |
3、使用案例
3.1、锁的基本使用
java
public class Test04 {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(()->{
counter.add();
});
Thread t2 = new Thread(()->{
counter.dec();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.getValue());
}
}
class Counter{
private static int value = 0;
// 创建锁对象
private final ReentrantLock lock =new ReentrantLock();
public static int getValue() {
return value;
}
public void add(){
lock.lock();
try {
for (int i =0 ;i <10000;i++){
value++;
}
} finally {
lock.unlock();
}
} public void dec(){
lock.lock();
try {
for (int i =0 ;i <10000;i++){
value--;
}
} finally {
lock.unlock();
}
}
}
3.2、支持中断锁的获取
java
public class demo1 {
public static void main(String[] args) throws InterruptedException {
// 创建ReentrantLock锁对象
ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(()->{
try {
lock.lockInterruptibly();
try {
System.out.println("线程任务执行!");
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("线程中断!");
return;
}
System.out.println("子线程结束!");
});
t1.start();
t1.interrupt();
System.out.println("主线程结束!");
}
}
主线程结束!
线程中断!
4、synchronized和synchronized的区别
|---------|-----------------|----------------------------|
| 特性 | Synchronized | synchronized |
| 锁的获取与释放 | 隐式调用,由JVM自动完成 | 显示调用,手动调用lock()和unlock()方法 |
| 可中断性 | 不可中断 | 可以中断,使用lockInterruptibly() |
| 超时机制 | 不支持 | 支持,使用tryLock(timeout) |
| 公平性 | 非公平 | 非公平和公平都支持 |
| 条件变量 | 单一wait/notify | 支持条件对象Condition |
| 锁状态检测 | 无法判断 | 通过方法可以获取 |
| 锁实现机制 | 监听器 Monitor | AQS |