Java锁:Lock与synchronized的全面对比

Java锁:Lock与synchronized的全面对比

1. 基本概念

synchronized

synchronized是Java内置的关键字,是最基本的线程同步机制,它可以用于方法或代码块:

// 同步方法
public synchronized void method() {
    // ...
}

// 同步代码块
public void method() {
    synchronized(this) {
        // ...
    }
}

原理图
在这里插入图片描述

Lock接口

Lock是Java 5引入的显式锁接口,位于java.util.concurrent.locks包中,主要实现类是ReentrantLock

Lock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // ...
    } finally {
        lock.unlock();
    }
}

2. 主要区别

特性synchronizedLock
实现方式JVM内置关键字Java类实现
获取与释放自动获取和释放需要显式调用lock()和unlock()
尝试非阻塞获取锁不支持支持tryLock()
可中断性不支持中断支持lockInterruptibly()
公平性非公平可配置公平或非公平
条件变量一个隐式条件(通过wait/notify)可创建多个Condition对象
性能早期版本性能较差,后续优化通常性能更好
代码复杂度简单更复杂,需要确保释放锁

3. 优缺点分析

synchronized的优点

  1. 使用简单:语法简洁,无需手动释放锁
  2. 自动释放:在代码块或方法执行完毕后自动释放锁
  3. JVM优化:JVM会对synchronized进行优化,如锁消除、锁粗化、偏向锁、轻量级锁等
  4. 内置特性:与wait()/notify()机制天然集成

synchronized的缺点

  1. 功能有限:无法实现尝试获取锁、定时获取锁、可中断获取锁等高级功能
  2. 不可中断:一旦进入阻塞状态,无法被中断
  3. 单一条件:只能有一个隐式的等待条件
  4. 非公平性:锁获取总是非公平的

Lock的优点

  1. 功能丰富:支持尝试获取、定时获取、可中断获取等
  2. 公平性可选:可以创建公平锁
  3. 多条件变量:可以创建多个Condition对象
  4. 性能优势:在高竞争环境下通常表现更好
  5. 可扩展性:可以基于Lock接口实现更复杂的同步机制

Lock的缺点

  1. 使用复杂:需要手动获取和释放锁,容易忘记释放
  2. 编码要求高:通常需要在finally块中释放锁
  3. 无自动优化:没有JVM内置的优化机制

4. 使用场景

适合使用synchronized的场景

  1. 简单的同步需求:只需要基本的互斥访问控制
  2. 锁持有时间短:锁的持有时间很短,竞争不激烈
  3. 代码简洁优先:希望代码尽可能简洁明了
  4. 与wait/notify配合:需要使用内置的等待/通知机制

适合使用Lock的场景

  1. 需要高级功能:如尝试获取锁、定时获取锁、可中断获取锁等
  2. 公平性要求:需要公平的锁获取机制
  3. 多条件变量:需要多个等待条件
  4. 高竞争环境:锁竞争激烈,需要更好的性能
  5. 跨越方法边界:需要在不同方法中获取和释放锁

5. 性能考虑

在Java 6及以后版本中,synchronized经过大量优化,在低竞争环境下性能与Lock相当。但在高竞争环境下,Lock通常表现更好,特别是:

  • 当使用ReentrantLock的公平模式时
  • 当需要尝试获取锁(tryLock)时
  • 当锁竞争非常激烈时

6. 最佳实践

  1. 优先考虑synchronized:对于简单场景,优先使用synchronized,它更简单且不易出错
  2. 需要高级功能时使用Lock:当synchronized无法满足需求时再考虑Lock
  3. 确保释放Lock:使用try-finally确保Lock被释放
  4. 避免过早优化:不要为了可能的性能提升而过度使用Lock
  5. 考虑读写锁:读多写少场景考虑使用ReentrantReadWriteLock

7. 示例代码对比

基本同步

// synchronized
public class Counter {
    private int count;
    
    public synchronized void increment() {
        count++;
    }
}

// Lock
public class Counter {
    private int count;
    private Lock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
}

尝试获取锁

// 只有Lock支持
public boolean tryIncrement() {
    if (lock.tryLock()) {
        try {
            count++;
            return true;
        } finally {
            lock.unlock();
        }
    }
    return false;
}

条件变量

// synchronized - 单一条件
synchronized(lockObj) {
    while (!condition) {
        lockObj.wait();
    }
    // ...
    lockObj.notifyAll();
}

// Lock - 多条件
Condition condition = lock.newCondition();
lock.lock();
try {
    while (!someCondition) {
        condition.await();
    }
    // ...
    condition.signal();
} finally {
    lock.unlock();
}

结论

synchronizedLock各有优劣,选择哪种锁机制取决于具体需求。对于大多数简单场景,synchronized是更安全、更简洁的选择。当需要更高级的同步功能时,Lock提供了更大的灵活性和控制能力。理解两者的区别和适用场景,可以帮助开发者编写出更高效、更健壮的并发代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值