简述
synchronized是什么?
synchronized 关键字是一种同步锁,它可以保证在一个时刻只有一个线程可以执行某段代码。synchronized 关键字可以用在方法、代码块、静态方法和静态代码块上。
synchronized怎么用?
synchronized是Java中用于实现线程同步的关键字,它可以修饰方法或代码块。
- 修饰方法:当一个方法被synchronized修饰时,表示该方法是一个同步方法。同一时间只能有一个线程执行该方法,其他线程需要等待。
public synchronized void method() {
// 这里是同步代码块
}
- 修饰代码块:当一个代码块被synchronized修饰时,表示该代码块是一个同步代码块。同一时间只能有一个线程进入该代码块,其他线程需要等待。
public class Example {
public static void main(String[] args) {
final int[] number = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
new Thread(() -> {
synchronized (number) {
// 这里是同步代码块
System.out.println(number[i]);
}
}).start();
}
}
}
锁的是什么
普通同步方法>锁的是当前实力对象。
静态同步方法>锁的是当前类的Class对象。
同步方法快>锁的是synchonized括号里配置的对象。
注意事项
当使用synchronized修饰代码块时,应该尽量控制同步代码块的范围,避免锁的竞争过于频繁,以提高程序的性能。同时,锁对象的选择也很重要,应该选择合适的锁对象以避免不必要的线程等待和资源竞争。
原理
synchronized修饰代码块和方法的底层原理类似,都是通过对象头中的锁标记位来实现的。
修饰方法
进入方法:当线程调用一个被synchronized修饰的方法时,会尝试获取该方法所属对象的锁。
获取锁:如果对象的锁标记位为"unlocked"状态,则当前线程可以获取到锁,并将锁标记位设置为"locked"状态,表示该对象被当前线程锁定。
执行方法体:线程获取到锁后,会执行synchronized修饰的方法体中的代码。
释放锁:当方法执行完毕或抛出异常时,会自动释放对象的锁,将锁标记位重新设置为"unlocked"状态。
注意点
需要注意的是,synchronized修饰方法时,默认锁定的是当前对象实例(this),即当前方法所属的对象。如果是静态方法,锁定的是当前类的Class对象。
//1.静态方法
public static synchronized void method() {
// 该方法被当前类的Class对象锁定
// ...
}
//2.普通方法
public synchronized void method() {
// 该方法被当前实例对象锁定
// ...
}
修饰代码块
进入代码块:当线程进入一个被synchronized修饰的代码块时,会尝试获取对象的锁。
获取锁:如果对象的锁标记位为"unlocked"状态,则当前线程可以获取到锁,并将锁标记位设置为"locked"状态,表示该对象被当前线程锁定。
阻塞或执行:如果对象的锁标记位为"locked"状态,表示该对象已被其他线程锁定。当前线程会被阻塞,直到锁标记位变为"unlocked"状态时才能继续执行。
释放锁:当线程执行完synchronized代码块后,会释放对象的锁,将锁标记位重新设置为"unlocked"状态,使其他线程能够获取到锁并执行。
public void method() {
synchronized (lock) {
// 该代码块被lock对象锁定
// ...
}
}
锁的高级特征
JVM在实现锁的过程中采用了多种锁的机制,包括偏向锁、轻量级锁和重量级锁,并且会根据锁竞争的情况自动进行锁升级和降级。下面是对这些锁的机制和锁升级的简要说明:
偏向锁(Bias Locking):
简介
偏向锁是一种乐观锁策略,适用于大部分情况下只有一个线程对锁进行竞争的场景。
偏向锁的目标是减少无竞争的情况下对锁的开销,提高程序的性能。
当一个线程获取到偏向锁后,JVM会将线程的标识记录在对象头中,之后该线程再次获取锁时无需进行同步操作,从而提高了程序的执行效率。
实现原理
轻量级锁(Lightweight Locking):
简介
当多个线程对同一个锁进行竞争时,JVM会将锁升级为轻量级锁。
轻量级锁使用CAS(Compare and Swap)操作来实现对锁的获取和释放,避免了线程阻塞和唤醒的开销。
如果竞争激烈,多个线程同时尝试获取锁,那么轻量级锁会膨胀为重量级锁。
实现原理
重量级锁(Heavyweight Locking):
简介
当轻量级锁膨胀失败或竞争过于激烈时,JVM会将锁升级为重量级锁。
重量级锁使用操作系统的互斥量来实现对锁的获取和释放,需要涉及线程的阻塞和唤醒。
线程在获取重量级锁时会进入阻塞状态,当锁被释放时,JVM会从阻塞的线程中选择一个进行唤醒。
锁的升级和降级是根据锁竞争的情况动态进行的,以提高程序的性能和吞吐量。当JVM检测到锁竞争较少时,会尝试将重量级锁降级为轻量级锁,以提高并发性能。反之,如果锁竞争激烈,JVM会将轻量级锁膨胀为重量级锁,以避免不必要的自旋和消耗。
实现原理
需要注意的是,锁的升级和降级过程对于开发者来说是透明的,无需手动干预。JVM会根据实际情况自动进行锁的升级和降级操作,以达到更好的性能和可伸缩性。
这些锁的机制和锁升级是JVM内部的实现细节,对于开发者来说,只需要了解它们的存在和基本原理,以正确地使用synchronized关键字来实现线程安全的同步。
优秀文章传送门:
https://zhuanlan.zhihu.com/p/571793506