1. 方法签名和行为对比

// lock() 方法:阻塞式获取锁
public void lock() {
    sync.lock();  // 调用AQS的acquire(1)方法
}

// tryLock() 方法:非阻塞式尝试获取锁
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);  // 尝试获取,不阻塞
}

// tryLock(long timeout, TimeUnit unit) 方法:带超时的尝试获取锁
public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

2. 核心区别

特性 lock() tryLock()
阻塞行为 阻塞当前线程,直到获得锁 不阻塞,立即返回
返回值 无返回值(void) 返回boolean值(true/false)
超时控制 无超时 可选超时(tryLock(long, TimeUnit))
异常抛出 无异常 无异常(失败返回false)

3. 详细实现对比

lock() 方法实现:
public void lock() {
    sync.lock();  // AQS中的acquire(1)方法
  
    // acquire(1)内部逻辑:
    // 1. 尝试CAS设置状态(非阻塞)
    // 2. 如果失败,进入队列等待
    // 3. 等待被唤醒后重新尝试获取锁
}
tryLock() 方法实现:
public boolean tryLock() {
    return sync.nonfairTryAcquire(1);  // 底层基于AQS中的compareAndSetState方法
  
    // nonfairTryAcquire内部逻辑:
    // 1. 尝试CAS设置状态(非阻塞)
    // 2. 成功则返回true
    // 3. 失败则返回false(不阻塞)
}

4. 实际使用场景

lock() 使用示例:
ReentrantLock lock = new ReentrantLock();

lock.lock();  // 阻塞直到获得锁
try {
    // 执行临界区代码
} finally {
    lock.unlock();
}
tryLock() 使用示例:
ReentrantLock lock = new ReentrantLock();

if (lock.tryLock()) {  // 尝试获取锁
    try {
        // 执行临界区代码
    } finally {
        lock.unlock();  // 必须解锁
    }
} else {
    // 锁已被其他线程持有,继续执行其他逻辑
    System.out.println("锁不可用,执行其他任务");
}

5. 性能和适用场景

lock() 适合:
  • 必须获得锁:业务逻辑依赖锁资源
  • 高竞争环境:锁可能长时间被占用
  • 同步语义明确:需要保证原子性操作
tryLock() 适合:
  • 可选锁:锁不是必需的
  • 高并发环境:避免长时间阻塞
  • 重入锁:需要多次尝试获取锁
  • 组合策略:与其他条件判断结合使用

6. 注意事项

tryLock() 的潜在问题:
// 错误示例:忘记检查返回值
if (lock.tryLock()) {  // 正确,检查返回值
    // 执行临界区
} else {
    // 处理锁不可用的情况
}

// 错误示例:忽略tryLock()返回值
lock.tryLock();  // 错误,没有检查是否成功获取锁
// 如果锁不可用,会立即返回false,但程序继续执行
lock() 的潜在问题:
// 错误示例:忘记unlock()
lock.lock();
// 执行临界区
// 忘记unlock() → 导致死锁或资源泄漏!

// 正确做法:使用try-finally确保解锁
lock.lock();
try {
    // 执行临界区
} finally {
    lock.unlock();  // 保证总是解锁
}

7. 完整对比示例

public class LockComparisonExample {
    private final ReentrantLock lock = new ReentrantLock();
  
    public void methodWithLock() {
        lock.lock();  // 阻塞直到获得锁
        try {
            // 执行临界区代码
            System.out.println(Thread.currentThread().getName() + " 获取锁");
            Thread.sleep(1000);  // 模拟耗时操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();  // 释放锁
        }
    }
  
    public void methodWithTryLock() {
        if (lock.tryLock()) {  // 尝试获取锁
            try {
                // 执行临界区代码
                System.out.println(Thread.currentThread().getName() + " 获取锁");
                Thread.sleep(1000);  // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();  // 释放锁
            }
        } else {
            // 锁不可用,执行其他逻辑
            System.out.println(Thread.currentThread().getName() + " 锁不可用");
        }
    }
}

8. 总结

对比维度 lock() tryLock()
阻塞性 阻塞当前线程 不阻塞,立即返回
返回值 void boolean
超时控制 可选超时(tryLock(long, TimeUnit))
适用场景 必须获得锁 可选锁、高并发、避免阻塞
错误处理 必须在finally中解锁 必须检查返回值

简单记忆

  • lock()就像排队等车,必须等到有座位才走
  • tryLock()就像问"有没有空位",有就坐,没有就继续干别的

如果锁是必需品,用lock()
如果锁可以等待或不需要,用tryLock()

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐