目录
什么是synchronized
在Java中,synchronized
关键字是一种同步机制,用于控制多个线程对共享资源的访问,以保证在任意时刻只有一个线程可以执行特定的代码段。
synchronized锁用法
同步方法
在方法声明中使用 synchronized
关键字,可以将整个方法标记为同步方法。当一个线程访问该同步方法时,其他线程将无法同时访问该对象的任何同步方法。
1.实例方法
这种方式是将当前实例对象作为锁对象,即 this
。
public synchronized void instanceMethod() {
// 方法体
}
例如:
public class Counter {
private int count = 0;
// 同步实例方法
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
increment
和 getCount
都是同步实例方法。当多个线程同时访问 Counter
对象时,它们必须依次执行这两个方法,以确保 count
的正确性。
2.静态方法
这种方式是将当前类的 Class
对象作为锁对象。
public static synchronized void staticMethod() {
// 方法体
}
例如:
public class Singleton {
private static Singleton instance;
// 同步静态方法
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
getInstance
是同步静态方法。当多个线程同时调用 getInstance
时,它们必须依次执行,以确保 instance
只被创建一次,实现线程安全的单例模式。
同步代码块
在代码块中使用 synchronized
关键字,可以更细粒度地控制同步范围,只对代码块中的代码进行同步。
1.同步实例对象
这种方式将当前实例对象作为锁对象。
public void someMethod() {
synchronized (this) {
// 只有这部分代码是同步的
}
}
例如:
public class BankAccount {
private double balance;
public void deposit(double amount) {
synchronized (this) { // 同步实例对象
if (amount > 0) {
balance += amount;
}
}
}
public void withdraw(double amount) {
synchronized (this) { // 同步实例对象
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}
}
deposit
和 withdraw
方法中使用同步代码块,将当前实例对象 this
作为锁对象。这样可以确保在修改 balance
时,同一时刻只有一个线程能够执行这些操作,避免并发问题。
2.同步任意对象
这种方式可以使用任意对象作为锁对象,适用于更复杂的同步控制场景。
private final Object lock = new Object();
public void anotherMethod() {
synchronized (lock) {
// 只有这部分代码是同步的
}
}
例如:
public class Cache {
private final Map<String, Object> cacheMap = new HashMap<>();
private final Object lock = new Object();
public void put(String key, Object value) {
synchronized (lock) { // 同步任意对象
cacheMap.put(key, value);
}
}
public Object get(String key) {
synchronized (lock) { // 同步任意对象
return cacheMap.get(key);
}
}
}
put
和 get
方法中使用同步代码块,将一个任意对象 lock
作为锁对象。这样可以对 cacheMap
的操作进行同步控制,确保在多个线程访问缓存时,数据的一致性和完整性。
四种用法总结
同步实例方法和同步静态方法适用于简单的同步需求,但可能导致性能问题。
同步代码块提供了更细粒度的同步控制,可以减少不必要的同步开销。
同步任意对象的方式提供了更灵活的同步控制,适用于复杂的同步场景。
synchronized锁升级
锁升级(Lock Coarsening)是JVM对synchronized
锁进行优化的一种机制。锁升级的目的是减少锁操作的开销,提高多线程程序的性能。
在JDK 1.6之后,JVM为了提⾼锁的获取与释放效率,对synchronized
的实现进⾏了优化,引⼊了偏向锁和轻量级锁,从此以后Java内置锁的状态就有了4种(无锁、偏向锁、轻量级锁和重量级锁),并且4种 状态会随着竞争的情况逐渐升级,⽽且是不可逆的过程,即不可降级,也就是说只能进⾏锁升级(从低级别到⾼级别)。
MarkWord
Mark Word(标记字段)是Java对象头(Object Header)的一部分,它用于存储对象的运行时数据,例如哈希码、分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
无锁(Unlock)
概念:无锁是指对象头中的锁标志位为01,表示对象没有被任何线程锁定。
特点:当没有任何线程尝试获取对象锁时,对象处于无锁状态。此时,对象可以被任意线程获取锁。
适用场景:适用于对象未被同步或者同步代码块未被任何线程执行的情况。
偏向锁(Biased Locking)
概念:偏向锁是JVM为了提高锁的获取效率而引入的一种锁状态。当一个线程第一次获取对象锁时,JVM会将对象头中的锁标志位设置为偏向锁状态,并记录获取锁的线程ID。
特点:
偏向线程:偏向锁会偏向于第一次获取它的线程。当该线程再次获取锁时,不需要进行任何同步操作,直接成功。
撤销偏向:如果其他线程尝试获取偏向锁,JVM会撤销偏向锁状态,并根据竞争情况升级为轻量级锁或重量级锁。
适用场景:适用于锁被同一线程多次获取的场景,可以减少锁的获取和释放开销。
轻量级锁(Lightweight Lock)
概念:轻量级锁是当两个或多个线程交替执行同步块时,为了减少线程阻塞的开销而引入的一种锁状态。
特点:
CAS操作:轻量级锁使用CAS(Compare-And-Swap)操作来尝试获取锁。如果对象头中的锁标志位为00,表示对象处于无锁状态,线程会尝试用CAS操作将对象头中的锁标志位改为轻量级锁状态。
自旋等待:当一个线程获取轻量级锁后,其他线程尝试获取锁时会进入自旋等待状态。自旋等待是一种忙等待,线程不会进入阻塞状态,而是不断尝试获取锁。
锁膨胀:如果自旋等待的线程太多或者自旋时间过长,轻量级锁会膨胀为重量级锁。
适用场景:适用于锁竞争不激烈且同步块执行时间较短的场景,可以减少线程阻塞的开销。
重量级锁(Heavyweight Lock)
概念:重量级锁是传统的锁机制,当轻量级锁膨胀或者锁竞争激烈时,锁会升级为重量级锁。
特点:
阻塞队列:重量级锁使用操作系统的阻塞队列来管理等待锁的线程。当一个线程获取重量级锁后,其他线程尝试获取锁时会被阻塞,进入阻塞队列等待。
上下文切换:重量级锁会导致线程的上下文切换,线程从运行状态变为阻塞状态,再从阻塞状态变为运行状态需要进行上下文切换,开销较大。
适用场景:适用于锁竞争激烈的场景,可以保证线程间的互斥访问。
锁状态的升级过程
无锁 -> 偏向锁:当一个线程第一次获取对象锁时,如果没有其他线程竞争,锁会从无锁状态升级为偏向锁状态。
偏向锁 -> 轻量级锁:当其他线程尝试获取偏向锁时,偏向锁状态会被撤销,并升级为轻量级锁状态。
轻量级锁 -> 重量级锁:当轻量级锁的自旋等待线程太多或者自旋时间过长时,锁会膨胀为重量级锁状态。
注意:锁状态的升级是不可逆的:锁状态只能从无锁升级到偏向锁、轻量级锁、重量级锁,不能从重量级锁降级到轻量级锁或偏向锁。
锁优化的目的是提高性能:JVM通过锁状态的升级和优化,减少了锁的获取和释放开销,提高了多线程程序的性能。开发者在编写代码时,不需要显式地管理锁状态,JVM会根据实际情况自动进行优化。
synchronized锁特点
原子性
当一个线程进入被 synchronized
修饰的代码块时,其他线程无法同时进入该代码块。这确保了代码块内的操作是原子性的,即要么全部执行,要么全部不执行,不会出现部分执行的情况。
可见性
当一个线程修改了被 synchronized
修饰的共享变量后,其他线程能够立即看到这个修改。这是因为 synchronized
会将共享变量的修改强制刷新到主内存中,其他线程在获取锁时会从主内存中读取最新的值。
可重入
synchronized
会维护一个锁持有计数来追踪同一个线程对锁的多次获取。每次线程获取锁时,计数加一;每次释放锁时,计数减一。只有当计数归零时,其他线程才能获取该锁。
简单易用
synchronized
的使用语法简单明了,可以通过同步方法或同步代码块的方式轻松实现线程同步。