线程同步
线程同步与通信
线程同步
线程同步是指多个线程在访问共享资源时,通过某种机制确保同一时间只有一个线程可以访问该资源,从而避免数据不一致或竞态条件(Race Condition)的发生
-
synchronized 关键字
-
synchronized 是Java中最常用的同步机制。它可以用于方法或代码块,确保同一时间只有一个线程可以执行被synchronized修饰的代码
-
当一个线程进入synchronized方法时,它会获取该方法所属对象的锁,其他线程必须等待锁释放后才能进入
public synchronized void method() { // 同步方法 // 实例方法的锁对象为当前实例对象(this) // 静态方法的锁对象为当前类的 Class 对象 } public void method() { synchronized (this) { // 同步代码块 //同步代码块允许更细粒度的控制,可以指定锁对象(如this、Object等) } }
-
-
ReentrantLock
-
ReentrantLock是java.util.concurrent.locks包中的一个类,提供了比synchronized更灵活的锁机制
-
ReentrantLock允许更复杂的锁操作,如尝试获取锁、超时获取锁、可中断获取锁等
ReentrantLock lock = new ReentrantLock(); public void method() { lock.lock(); // 获取锁 try { // 线程安全的代码 } finally { lock.unlock(); // 释放锁 } }
-
-
volatile 关键字
-
volatile用于修饰变量,确保变量的可见性。当一个线程修改了volatile变量的值,其他线程可以立即看到修改后的值
-
当变量被声明为 volatile 时,任何线程修改该变量的值都会 立即刷新到主内存,其他线程读取该变量时会 直接从主内存获取最新值,而非使用本地缓存(如 CPU 缓存)
volatile boolean flag = false; // 线程 A flag = true; // 修改后其他线程立即可见 // 线程 B while (!flag) { /* 循环直到 flag 变为 true */ } //若不使用 `volatile`,线程 B 可能因缓存不一致陷入死循环 -
volatile不保证原子性,因此它通常用于简单的标志位或状态变量
禁止指令重排序
-
JVM 和 CPU 可能对代码执行顺序进行优化(指令重排序),但 volatile 通过插入 内存屏障(Memory Barrier)阻止这种优化,确保代码执行顺序符合预期
-
在单例模式的双重检查锁定(Double-Checked Locking)中,若实例变量不用 volatile 修饰,可能导致其他线程获取未初始化完全的对象
public class Singleton { private static Singleton instance; // 未使用 volatile public static Singleton getInstance() { if (instance == null) { // 第一次检查(无锁) synchronized (Singleton.class) { if (instance == null) { // 第二次检查(加锁后) instance = new Singleton(); // 问题根源在此 } } } return instance; } } /* Java 对象的创建过程可分为三步: 1. 分配内存空间(为对象分配堆内存); 2. 初始化对象(调用构造函数、设置字段初始值); 3. 将引用指向内存地址(赋值给变量 instance)。 JVM 允许对步骤 2 和步骤 3 进行指令重排序。若发生重排序,其他线程可能在对象未完成初始化时,看到 instance 已非空,从而直接使用一个部分初始化的对象1。 2. 内存可见性问题 即使未发生指令重排序,若 instance 未用 volatile 修饰,线程可能从本地缓存中读取过期的 instance 值(例如线程 A 完成初始化后,线程 B 仍认为 instance 为 null),导致重复创建对象 */
-
synchronized锁对象的释放
- 同步方法或者代码块执行结束
- 同步方法或者代码块执行出现未处理的错误,导致异常结束该线程
- 调用锁对象的wait()方法

浙公网安备 33010602011771号