多线程中的锁 —— synchronized

时间:2025-04-02 10:45:32

文章目录

  • ⭐synchronized(内置锁)
    • 1. synchronized 的基本使用
      • 1.1 修饰静态方法
      • 1.2 修饰普通方法
      • 1.3 修饰代码块
    • 2. synchronized 的特性
      • 2.1 互斥性(原子性)
      • 2.2 刷新内存(可见性)
      • 2.3 可重入(有序性)
    • 3. synchronized 的底层实现原理


使用锁是 Java 中解决线程安全问题中最主要的手段。

⭐synchronized(内置锁)

synchronized 是 Java 中的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,而这段代码也被称为临界区

1. synchronized 的基本使用

synchronized 的基本用法有以下三种:

  1. 修饰静态方法
  2. 修饰普通方法
  3. 修饰代码块

1.1 修饰静态方法

synchronized 修饰的方法是静态方法时,则是类锁。

public synchronized static int countData(int data){
      return data*data;
}
  • 1
  • 2
  • 3

1.2 修饰普通方法

synchronized 修饰的方法是普通成员方法时,则是对象锁,就是是以当前对象为锁,即调用这个方法的对象。

public synchronized void incr() {
	for(int i = 0; i < MAX_COUNT; i++) {
		number++;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.3 修饰代码块

修饰代码块时,加锁的对象有三种:

public void decr() {
	synchronized (object) {
	}
}
  • 1
  • 2
  • 3
  • 4

是 this 时,是对象锁,可以在非静态方法中使用,不能在静态方法中使用。

public static void test(){
	synchronized (this) {
	}
}
  • 1
  • 2
  • 3
  • 4

是类名.class 时,是一个类的 class 对象,就是类锁。可以在静态、非静态方法中使用。

public void test() {
	synchronized (类名.class) {
	}
}
  • 1
  • 2
  • 3
  • 4

3.自定义锁对象, object 是一个对象实例。
如果是静态对象,就是类锁;如果是非静态对象,就是对象锁。

public void test() {
	Count count = null;
	synchronized (count) {
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5

注意事项:加锁时一定要注意对于同一个业务的多个线程加锁对象,一定要是同一个对象。

2. synchronized 的特性

2.1 互斥性(原子性

synchronized 会起到互斥效果,某个线程执行到某个对象的 synchronized 中时,其他线程如果也执行到同一个对象的 synchronized 就会阻塞等待。起到了原子性的作用,确保线程互斥的访问同步代码。

2.2 刷新内存(可见性)

synchronized 工作时,先获得互斥锁,再从主内存拷贝变量的最新副本到工作内存中,执行代码,将更改后的共享变量的值刷新到主内存中,然后释放互斥锁。这样就保证了内存可见性

2.3 可重入(有序性)

synchronized 同步块对同一条线程来说是可重入的,有效的解决了重排序问题,即一个变量在同一时刻只允许一条线程对其进行 lock 操作,实现了有序性

3. synchronized 的底层实现原理

在 JVM 层面,synchronized 同步锁是依靠监视器 monitor 实现的。

synchronized 同步代码块的实现是通过 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。当 jvm执行到 monitorenter 指令时,当前线程试图获取monitor 对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行 monitorexit 指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取 monitor 对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。

synchronized 修饰的方法是隐式的,无需通过字节码指令来控制,所以没有 monitorenter 指令和 monitorexit 指令,取而代之的 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

活动地址:****21天学习挑战赛