1657 字
约 5 分钟
10
Java JUC教程第3章:synchronized关键字

Java JUC教程第3章:synchronized关键字

章节头部 第3章

🔒 synchronized关键字

深入理解Java最基础的同步机制,掌握对象锁、类锁和锁升级原理
学习目标

学习目标

  • 深入理解synchronized的工作原理
  • 掌握对象锁和类锁的区别
  • 了解锁升级机制
  • 学会识别和避免死锁
  • 掌握synchronized的性能优化技巧

synchronized原理

synchronized原理

synchronized是Java提供的最基础的同步机制,它基于监视器锁(Monitor Lock)实现。每个Java对象都有一个内置的监视器锁,当线程进入synchronized代码块或方法时,会自动获取这个锁。

核心概念

synchronized关键字保证了同一时刻只有一个线程可以执行被同步的代码,从而确保线程安全。

监视器锁机制

获取锁线程进入synchronized代码块时,尝试获取对象的监视器锁。如果锁可用,则获取锁并继续执行。等待锁如果锁被其他线程持有,当前线程会被阻塞,进入等待状态,直到锁被释放。释放锁线程执行完synchronized代码块后,会自动释放锁,唤醒等待的线程。

字节码分析

从字节码层面看,synchronized的实现使用了monitorenter和monitorexit指令:
字节码示例

// Java代码
public void synchronizedMethod() {
    synchronized(this) {
        // 同步代码块
    }
}

// 对应字节码
monitorenter    // 获取锁
// 同步代码
monitorexit     // 释放锁
monitorexit     // 异常情况下的释放锁

对象锁和类锁

对象锁和类锁

synchronized可以修饰实例方法、静态方法和代码块,根据修饰的目标不同,获取的锁也不同。

对象锁(实例锁)

实例方法同步示例代码

public synchronized void instanceMethod() {
    // 获取当前对象的锁
}

代码块同步示例代码

public void method() {
    synchronized(this) {
        // 获取当前对象的锁
    }
}

类锁(静态锁)

静态方法同步示例代码

public static synchronized void staticMethod() {
    // 获取类的Class对象的锁
}

Class对象同步示例代码

public void method() {
    synchronized(MyClass.class) {
        // 获取类的Class对象的锁
    }
}

重要区别

对象锁和类锁是完全独立的,一个线程获取了对象锁,不会阻止其他线程获取类锁。
锁升级机制

锁升级机制

为了提高性能,JVM对synchronized进行了优化,引入了锁升级机制。锁会根据竞争情况从偏向锁升级到轻量级锁,再升级到重量级锁。
偏向锁

  • 适用于只有一个线程访问的情况
  • 几乎没有性能开销
  • 在对象头中记录线程ID
  • 当有其他线程竞争时升级

轻量级锁

  • 适用于线程交替执行的情况
  • 使用CAS操作避免阻塞
  • 在栈帧中创建锁记录
  • 竞争激烈时升级为重量级锁

重量级锁

  • 适用于高竞争的情况
  • 使用操作系统的互斥量
  • 线程会被阻塞和唤醒
  • 性能开销最大

升级过程

偏向锁 → 轻量级锁 → 重量级锁(单向升级,不可降级)
死锁问题

死锁问题

死锁是多线程编程中的经典问题,当两个或多个线程相互等待对方释放锁时,就会发生死锁。

死锁的四个必要条件

互斥条件资源不能被多个线程同时使用持有和等待线程持有资源的同时等待其他资源不可剥夺资源不能被强制从线程中剥夺循环等待存在线程资源的循环等待链

死锁示例

经典死锁场景

public class DeadlockExample {
    private static Object lock1 = new Object();
    private static Object lock2 = new Object();
    
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread 1: 获取lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock2) {
                    System.out.println("Thread 1: 获取lock2");
                }
            }
        });
        
        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread 2: 获取lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized (lock1) {
                    System.out.println("Thread 2: 获取lock1");
                }
            }
        });
        
        t1.start();
        t2.start();
    }
}

死锁预防策略

  • 锁排序:所有线程按相同顺序获取锁
  • 锁超时:使用tryLock()设置获取锁的超时时间
  • 死锁检测:定期检测死锁并采取恢复措施
  • 避免嵌套锁:尽量避免在持有锁的情况下获取其他锁

性能优化

性能优化

虽然synchronized使用简单,但在高并发场景下可能成为性能瓶颈。了解优化技巧有助于提升程序性能。

优化策略

减小锁粒度
将大锁拆分为多个小锁,减少锁竞争
示例

// 粗粒度锁
synchronized(this) {
    updateField1();
    updateField2();
}

// 细粒度锁
synchronized(lock1) { updateField1(); }
synchronized(lock2) { updateField2(); }

锁分离
读写分离,使用ReadWriteLock
示例

ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();

锁消除
JVM会自动消除不必要的锁
示例

// 局部变量,JVM会消除同步
StringBuffer sb = new StringBuffer();
sb.append("hello");
sb.append("world");

性能建议

尽量缩短同步代码块的执行时间避免在循环中使用synchronized考虑使用并发集合类替代同步集合在高并发场景下考虑使用Lock接口
实战案例

实战案例:银行转账

通过银行转账的例子,演示如何正确使用synchronized解决并发安全问题。
线程安全的银行账户

public class BankAccount {
    private double balance;
    private final Object lock = new Object();
    
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
    
    public void deposit(double amount) {
        synchronized (lock) {
            balance += amount;
            System.out.println("存入: " + amount + ", 余额: " + balance);
        }
    }
    
    public boolean withdraw(double amount) {
        synchronized (lock) {
            if (balance >= amount) {
                balance -= amount;
                System.out.println("取出: " + amount + ", 余额: " + balance);
                return true;
            }
            return false;
        }
    }
    
    public double getBalance() {
        synchronized (lock) {
            return balance;
        }
    }
    
    // 转账方法 - 避免死锁的实现
    public static void transfer(BankAccount from, BankAccount to, double amount) {
        // 按对象hashCode排序,避免死锁
        BankAccount firstLock = from.hashCode() < to.hashCode() ? from : to;
        BankAccount secondLock = from.hashCode() < to.hashCode() ? to : from;
        
        synchronized (firstLock.lock) {
            synchronized (secondLock.lock) {
                if (from.balance >= amount) {
                    from.balance -= amount;
                    to.balance += amount;
                    System.out.println("转账成功: " + amount);
                } else {
                    System.out.println("余额不足,转账失败");
                }
            }
        }
    }
}

章节导航 上一章:线程基础 返回目录 下一章:volatile关键字

Java JUC教程第3章:synchronized关键字
https://clxhxhhr.top/posts/3018/
作者
clxstart
发布于
2026-09-24
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。