java基础--25.多线程的改进--Lock显式地加锁和释放锁

本文介绍了JDK5中Lock接口及其子类ReentrantLock的使用方法,通过具体示例展示了如何利用Lock来替代传统的synchronized关键字实现线程间的同步控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDK5中Lock锁的使用

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

接口:Lock

  • 包含方法:

    • void lock() 获取锁
    • void unlock() 释放锁
  • 直接已知子类:ReentrantLock

代码改进:使用Lock进行加锁和释放锁的操作

将原来方法中的synchronized同步方法中包含的代码的头和尾用lock 和 unlock 进行锁的操作

public class SellTicket implements Runnable {

    private int tickets = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            //加锁
            lock.lock();

            if(tickets > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+ "正在出售第"+(tickets--)+"张票");
            }

            //释放锁
            lock.unlock();
        }
    }

}

可以看到,采用这样的方法,有助于理解代码

<think>我们正在讨论Java中的Lock接口,它提供了一种更灵活的同步机制。与synchronized关键字不同,Lock接口需要加锁。让我们通过分析引用内容来理解Lock接口的使用机制。引用[1]展示了一个使用Lock的示例:通过ReentrantLock实现的获取释放。引用[2]指出Lock是一个接口,而synchronized是内置的关键字,并提到Lock需要在finally块中释放以避免死。引用[3]则解释了ReentrantLock支持重入公平性选择。现在,我将详细解释Lock接口的加锁机制:1.**Lock接口的基本使用**-创建Lock实例(通常使用ReentrantLock-在访问共享资源前调用lock()方法获取-在finally块中调用unlock()方法释放2.**重入性**-同一个线程可以多次获取同一把(不会阻塞自己)-重入次数必须与释放次数匹配3.**公平性**-创建ReentrantLock时可指定公平策略-公平:等待时间最长的线程优先获取-非公平:允许线程"插队"4.**与synchronized的对比**-Lock提供更细粒度的控制(如可中断、超时获取等)-必须手动释放,否则会导致死示例代码演示:```javaimportjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassLockExample{privatefinalLocklock=newReentrantLock();//创建实例publicvoidsafeMethod(){lock.lock();//获取(阻塞直到成功)try{//临界区代码System.out.println(Thread.currentThread().getName()+"进入临界区");Thread.sleep(100);//模拟操作}catch(InterruptedExceptione){Thread.currentThread().interrupt();}finally{lock.unlock();//必须确保释放System.out.println(Thread.currentThread().getName()+"释放");}}publicstaticvoidmain(String[]args){LockExampleexample=newLockExample();//启动多个线程测试for(inti=0;i<3;i++){newThread(example::safeMethod).start();}}}```运行结果将示线程依次进入临界区,证明的互斥性:```Thread-0进入临界区Thread-0释放Thread-1进入临界区Thread-1释放Thread-2进入临界区Thread-2释放```**关键注意事项**:-必须在finally块中释放(确保异常时也能释放-避免将lock()调用放在try块内(防止未加锁就执行unlock-使用可中断时注意处理InterruptedException通过Lock接口,开发者可以更灵活地控制并发,但同时也增加了编程复杂度[^1][^2][^3]。§§相关问题§§1.Lock接口中tryLock()方法的工作原理是什么?2.如何通过Lock实现条件等待(Condition)?3.在分布环境中如何实现类似Lock的机制?</think>### JavaLock接口的加锁机制详解 Lock接口是Java并发包中提供的机制,与`synchronized`关键字相比,它提供了更灵活、更强大的线程同步功能。下面通过关键点解析其工作原理: #### 一、Lock接口核心方法 | 方法 | 作用 | 特点 | |--------------------|----------------------------------------------------------------------|--------------------------| | `lock()` | 获取,若被占用则阻塞等待 | 基础阻塞方法 | | `unlock()` | 释放 | **必须**在finally块调用 | | `tryLock()` | 尝试获取,立即返回结果 | 非阻塞,可避免死 | | `tryLock(timeout)` | 在指定时间内尝试获取 | 支持超时控制 | | `lockInterruptibly()` | 获取但允许被中断 | 响应线程中断请求 | #### 二、Lock接口工作流程 ```mermaid graph TD A[线程调用lock.lock] --> B{是否可用} B -->|可用| C[获取并继续执行] B -->|不可用| D[线程进入等待队列] C --> E[执行同步代码] E --> F[finally块调用lock.unlock] F --> G[唤醒等待队列中的线程] ``` #### 三、Lock接口关键特性 1. **控制** - 需要**手动调用`lock()``unlock()`** - 必须在`finally`块释放(引用[2]指出否则可能导致死)[^2] ```java Lock lock = new ReentrantLock(); lock.lock(); // 手动加锁 try { // 临界区代码 } finally { lock.unlock(); // 必须保证释放 } ``` 2. **可重入性** - 同一线程可重复获取同一把(引用[3]说明与`synchronized`类似)[^3] ```java public void nestedLock() { lock.lock(); // 第一次获取 try { lock.lock(); // 第二次获取(不会阻塞) // ... } finally { lock.unlock(); lock.unlock(); // 需匹配解次数 } } ``` 3. **公平性选择** - 通过构造函数控制公平策略: ```java Lock fairLock = new ReentrantLock(true); // 公平 Lock unfairLock = new ReentrantLock(); // 非公平(默认) ``` - 公平:按请求顺序分配(引用[3]解释等待时间最久者优先)[^3] 4. **高级功能** - 尝试机制: ```java if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒 try { /* 操作 */ } finally { lock.unlock(); } } else { // 处理无法获取的情况 } ``` - 可中断: ```java lock.lockInterruptibly(); // 可能抛出InterruptedException ``` #### 四、Lock vs synchronized | 特性 | Lock接口 | synchronized | |--------------------|-----------------------------------|----------------------------| | **获取方** | 调用`lock()` | 隐(进入同步块自动获取) | | **异常处理** | 需手动释放(否则死)[^2] | 自动释放 | | **公平性** | 可配置 | 非公平 | | **超时控制** | 支持`tryLock(timeout)` | 不支持 | | **中断响应** | 支持`lockInterruptibly()` | 不响应中断 | #### 五、最佳实践 1. **释放保证** 始终在`finally`块调用`unlock()`,避免因异常导致泄漏: ```java Lock lock = new ReentrantLock(); lock.lock(); try { // 可能抛出异常的代码 } finally { lock.unlock(); // 绝对执行 } ``` 2. **避免嵌套陷阱** 重入加锁必须严格匹配: ```java lock.lock(); lock.lock(); // 允许 try { ... } finally { lock.unlock(); // 需两次unlock lock.unlock(); } ``` 3. **优先使用tryLock** 在可能发生死的场景中使用超时控制: ```java if (lockA.tryLock(500, TimeUnit.MILLIS)) { try { if (lockB.tryLock(300, TimeUnit.MILLIS)) { // 成功获取双 } } finally { /* 释放 */ } } ``` > **核心总结**:Lock接口通过加锁/解操作,提供了比`synchronized`更精细的线程控制能力。其关键优势在于支持公平、可中断超时获取等高级特性(引用[1][^1][^3]),但需要开发者严格遵循"在finally块释放"的原则以避免死风险(引用[2])[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值