摘要:synchronized 是 Java 中最基本且最常用的线程同步机制,它能够有效解决多线程环境下的共享资源竞争问题。本文将全面解析 Synchronized 的工作原理、使用方式、优化策略以及实际应用场景。
synchronized 是 Java 中最基本且最常用的线程同步机制,它能够有效解决多线程环境下的共享资源竞争问题。本文将全面解析 Synchronized 的工作原理、使用方式、优化策略以及实际应用场景。
Synchronized 是 Java 语言原生支持的 关键字 ,用于实现线程同步,保证多线程环境下对共享资源的安全访问。它的核心作用可以概括为三点:
保证线程互斥访问 :同一时刻最多只有一个线程能执行被Synchronized修饰的方法或代码块,其他线程必须等待当前线程执行完毕后才能获取锁并执行 保证可见性 :确保一个线程对共享变量的修改能够立即对其他线程可见。在退出Synchronized块时,JVM会将工作内存中的变量刷新回主内存;在进入时,会强制从主内存重新读取变量值 保证有序性 :防止编译器和CPU对同步代码块内的指令进行重排序,确保代码按程序顺序执行 #技术分享不使用同步机制的典型后果是 数据不一致 。例如两个线程同时执行 count++操作,由于 count++实际上包含"读取-修改-写入"三个步骤,在并发情况下可能导致最终结果小于预期值
public synchronized void method {}这种方式锁住的是 当前实例对象 (this),同一个实例的多个同步方法会互斥,不同实例的同步方法不会相互干扰。例如银行账户操作:
public class BankAccount { private double balance; public synchronized void deposit(double amount) { balance += amount; } public synchronized void withdraw(double amount) { balance -= amount; }}public static synchronized void method {}这种方式锁住的是 类对象 (Class 对象),所有实例共享同一把锁,即使不同实例调用静态同步方法也会互斥。例如:
public class Counter { private static int count = 0; public static synchronized void increment { count++; }}synchronized(obj) {}这种方式可以灵活指定锁对象,obj 可以是任何对象实例。通常使用共享资源作为锁对象。例如:
public class TicketService { private int tickets; private final Object lock = new Object; public void sellTicket { synchronized(lock) { if(tickets > 0) { tickets--; } } }}synchronized(ClassName.class) {}这种方式同样锁住类对象,与静态同步方法效果相同。例如:
public class Logger { public static void log(String message) { synchronized(Logger.class) { } }}Synchronized 的底层实现依赖于 JVM 的 对象监视器锁(Monitor) 机制。每个 Java 对象都与一个 Monitor 相关联,Monitor 包含以下关键字段:
_owner :记录持有锁的线程_recursions :锁的重入次数_EntryList :存放等待锁的阻塞线程队列_WaitSet :存放调用wait后等待的线程队列当线程执行到同步代码块时:
执行 monitorenter 指令尝试获取Monitor所有权获取成功则进入同步代码块执行,Monitor进入数+1执行完毕后执行 monitorexit 指令,Monitor进入数-1当进入数为0时完全释放锁,唤醒等待线程对于同步方法,JVM 通过方法访问标志 ACC_SYNCHRONIZED 实现同步,原理与代码块同步类似
JDK1.6后对 Synchronized 进行了重大优化,引入了 锁升级 机制,根据竞争情况自动调整锁状态:
无锁状态 :初始状态,没有线程竞争锁 偏向锁 :当第一个线程访问同步块时,JVM会将对象头Mark Word标记为偏向该线程ID。后续该线程进入同步块时无需任何同步操作,只需检查线程ID是否匹配 轻量级锁 :当第二个线程尝试获取锁时,偏向锁升级为轻量级锁。线程通过 CAS操作 尝试获取锁,失败则短暂自旋等待,避免线程阻塞切换 重量级锁 :当多个线程竞争激烈时,轻量级锁升级为重量级锁。竞争失败的线程会进入阻塞状态,依赖操作系统线程调度机制锁升级是不可逆的,一旦升级为重量级锁就无法降级
Synchronized 是 可重入锁 ,同一线程在外层方法获取锁后,内层方法可以直接再次获取该锁。JVM 通过记录锁的持有线程和进入次数实现这一特性。例如:
public class ReentrantDemo { public synchronized void method1 { method2; } public synchronized void method2 { }}一旦锁被其他线程获取,当前线程只能选择等待或阻塞,无法主动中断获取锁的过程。这与 Lock 类的可中断特性形成对比
Synchronized 是 非公平锁 ,不保证等待时间最长的线程优先获取锁,任何尝试获取锁的线程都有机会立即获取锁
单例模式实现 :确保多线程环境下只创建一个实例public class Singleton { private static volatile Singleton instance; public static Singleton getInstance { if(instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton; } } } return instance; }} 线程安全的计数器 :保证计数的原子性public class SafeCounter { private int count; public synchronized void increment { count++; }} 资源池管理 :如数据库连接池的获取和释放public class ConnectionPool { private List pool = new ArrayList; public synchronized Connection getConnection { if(pool.isEmpty) return createConnection; return pool.remove(pool.size-1); } public synchronized void release(Connection conn) { pool.add(conn); }} 减小同步范围 :尽量只同步必要的代码块,而非整个方法 分离读写锁 :读多写少场景考虑使用读写锁替代 避免锁嵌套 :防止死锁发生 使用私有锁对象 :而非直接锁this或类对象,提高封装性public class PrivateLock { private final Object lock = new Object; public void method { synchronized(lock) { } }}Synchronized 作为 Java 最基本的同步机制,虽然存在一些性能缺陷,但在 JDK 不断优化下仍然是大多数并发场景的首选方案。理解其底层原理和适用场景,能够帮助开发者编写出更高效、更安全的并发程序。对于更复杂的并发需求,可以考虑结合 Lock、CAS 等机制实现
来源:墨码行者