跳转到内容

Java Lock接口

来自代码酷


Java Lock接口是Java并发编程中比`synchronized`关键字更灵活的线程同步机制,属于`java.util.concurrent.locks`包的核心组件。它提供了更细粒度的锁控制,支持公平锁、非阻塞尝试锁、可中断锁等高级特性。

概述[编辑 | 编辑源代码]

Lock接口定义了以下核心方法:

  • `lock()` - 获取锁(阻塞直到成功)
  • `unlock()` - 释放锁
  • `tryLock()` - 非阻塞尝试获取锁
  • `lockInterruptibly()` - 可中断地获取锁

与`synchronized`相比,Lock接口的主要优势包括:

  • 可定时的锁等待(`tryLock(long time, TimeUnit unit)`)
  • 公平锁与非公平锁的选择
  • 多个条件变量(`Condition`)支持
  • 更清晰的代码结构

基础用法[编辑 | 编辑源代码]

以下是最简单的Lock使用示例:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private final Lock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
        } finally {
            lock.unlock();  // 确保锁被释放
        }
    }
}

关键点说明: 1. 使用`ReentrantLock`作为Lock接口的实现类 2. `unlock()`必须放在`finally`块中确保释放 3. 锁的获取和释放必须成对出现

高级特性[编辑 | 编辑源代码]

1. 尝试获取锁[编辑 | 编辑源代码]

public boolean transferMoney(Account from, Account to, int amount) {
    // 尝试获取两个账户的锁
    if (from.getLock().tryLock()) {
        try {
            if (to.getLock().tryLock()) {
                try {
                    // 执行转账逻辑
                    return true;
                } finally {
                    to.getLock().unlock();
                }
            }
        } finally {
            from.getLock().unlock();
        }
    }
    return false;
}

2. 公平锁[编辑 | 编辑源代码]

flowchart TD A[线程1请求锁] --> B{锁可用?} B -->|是| C[线程1获取锁] B -->|否| D[进入等待队列] D --> E[按FIFO顺序唤醒]

创建公平锁:

Lock fairLock = new ReentrantLock(true);  // true表示公平锁

3. 条件变量[编辑 | 编辑源代码]

Lock可以关联多个`Condition`对象,实现精细的线程通信:

class BoundedBuffer {
    final Lock lock = new ReentrantLock();
    final Condition notFull = lock.newCondition(); 
    final Condition notEmpty = lock.newCondition();

    void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length)
                notFull.await();  // 等待"不满"条件
            // 放入元素
            notEmpty.signal();    // 通知"不空"条件
        } finally {
            lock.unlock();
        }
    }
}

性能考量[编辑 | 编辑源代码]

Lock与synchronized的性能对比:

锁性能比较(纳秒/操作)
场景 synchronized ReentrantLock
20 | 25
120 | 65
1500 | 300

结论:在高竞争环境下,Lock表现更好;低竞争时synchronized有JVM优化优势。

实际应用案例[编辑 | 编辑源代码]

数据库连接池实现

public class ConnectionPool {
    private final Lock lock = new ReentrantLock();
    private final Condition available = lock.newCondition();
    private final Set<Connection> pool = new HashSet<>();
    
    public Connection getConnection() throws InterruptedException {
        lock.lock();
        try {
            while (pool.isEmpty()) {
                available.await();
            }
            return pool.iterator().next();
        } finally {
            lock.unlock();
        }
    }
    
    public void releaseConnection(Connection conn) {
        lock.lock();
        try {
            pool.add(conn);
            available.signal();
        } finally {
            lock.unlock();
        }
    }
}

最佳实践[编辑 | 编辑源代码]

1. 总是使用`try-finally`确保锁释放 2. 避免在持有锁时调用外部方法(防止死锁) 3. 锁范围应尽可能小 4. 考虑使用`tryLock`避免死锁 5. 对读多写少的场景考虑`ReadWriteLock`

常见问题[编辑 | 编辑源代码]

Q: Lock和synchronized如何选择? A: 需要高级功能(如超时、公平性)时用Lock;简单场景用synchronized更简洁。

Q: 为什么Lock比synchronized快? A: 主要因为Lock在Java层面实现,而synchronized涉及JVM内部操作,但现代JVM对synchronized优化很多。

Q: Lock会导致内存泄漏吗? A: 不会直接导致,但忘记调用`unlock()`会导致线程永久阻塞。

数学原理[编辑 | 编辑源代码]

锁的公平性可以用排队论模型表示。设:

Twait=ρ1ρ×Tservice

其中:

  • ρ:系统利用率
  • Tservice:平均服务时间

公平锁保证Twait对所有线程一致。