线程concurrent包(Lock 與 Condition)

本文对比了synchronized和Lock的异同,详细介绍了Lock及其Condition接口如何提供更精细的线程控制和更好的性能。通过实例展示了如何使用ReentrantLock及Condition进行线程间的同步。

synchronized和java.util.concurrent.locks.Lock的异同

主要相同点:
Lock能完成synchronized所实现的所有功能,在synchronized中隱含著物件鎖定與釋放鎖定的觀念

主要不同点:
Lock有比synchronized更精确的线程语义和更好的性能 synchronized会自动释放锁.而Lock一定要求程序员手工释放.并且必须在finally从句
中释放

 

Lock 與 Condition
對於初學者來說,執行緒的同步化 並非是個容易理解的議題,在synchronized中隱含著物件鎖定與釋放鎖定的觀念,程式中並沒有明顯的語意來告知這一點,而必須靠程式設計人員本身記憶物件的鎖定與釋放鎖定問題。
在java.util.concurrent.locks套件中新增了Lock與Condition等類別,可以讓您明確的在程式中進行明確的鎖定與釋放鎖定。
Lock是一個介面,其中規範了lock()、unclock()與newCondition()三種方法:

  lock()  用來取得物件的鎖定。

  unlock()  用來釋放物件的鎖定,通常由同一個Lock物件來呼叫lock()與unlock()。

  newCondition()  建立一個與Lock物件相關聯的Conditon物件。

Condition是一個介面,作用是在執行緒之間進行溝通,就如其名稱所示,告知執行緒目前的狀況為何,是要等待?還是通知?其規範的幾個重要方法為:

await()  告知目前的執行緒等待,直到被通知或中斷(interrupted)。

signal()  通知目前等待中的一個執行緒,從上次的等待點繼續執行,類似物件的notify()方法

signalAll()  通知目前等待中的所有執行緒參與鎖定競爭,而後從上次的等待點繼續執行,類似物件的notifyAll()方法。

在這邊直接改寫wait ()、notify() 中的Clerk類別,不使用synchronized、wait()、notify(),而改用Lock與Condition,其中ReentrantLock為Lock介面的一個實作類別:

Clerk.java

import java.util.concurrent.locks.*;
public class Clerk {
    private Lock lock = new ReentrantLock();
    private Condition threadCond = lock.newCondition();
// -1 表示目前沒有產品
private int product = -1; 
// 這個方法由生產者呼叫
public void setProduct(int product) { 
lock.lock();
try {
if(this.product != -1) { 
try { 
// 目前店員沒有空間收產品,請稍候!
threadCond.await();
} 
catch(InterruptedException e) { 
e.printStackTrace(); 
} 
} 
this.product = product; 
System.out.printf("生產者設定 (%d)%n", this.product); 
// 通知等待區中的一個消費者可以繼續工作了
threadCond.signal();
}
finally {
   lock.unlock();
}
} 
// 這個方法由消費者呼叫
public int getProduct() { 
lock.lock();
int p = 0;
try {
if(this.product == -1) { 
try { 
// 缺貨了,請稍候!
 threadCond.await();
} 
catch(InterruptedException e) { 
e.printStackTrace(); 
} 
} 
p = this.product; 
System.out.printf(
"消費者取走 (%d)%n", this.product); 
this.product = -1; 
// 通知等待區中的一個生產者可以繼續工作了
threadCond.signal();

}

finally {

            lock.unlock();

}

return p; 

} 

} 

<think>我们正在讨论Java中的Lock接口及其使用,特别是java.util.concurrent.locks.Lock接口。根据引用[1],我们知道Lock接口(如ReentrantLock)是Java中常见的加锁方式之一,synchronized关键字相比,它提供了更灵活的锁控制。Lock接口的主要实现是ReentrantLock,它是一个可重入锁,意味着同一个线程可以多次获得同一把锁。Lock接口提供了以下关键方法:- lock():获取锁,如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获得锁。- unlock():释放锁。 -tryLock():尝试获取锁,如果锁可用则获取并立即返回true,否则返回false。此方法不会等待。 -tryLock(long time, TimeUnit unit):在给定的等待时间内尝试获取锁,如果获取到则返回true,否则超时后返回false。 -newCondition():返回绑定到此Lock实例的新Condition实例。 使用Lock接口的基本模式如下:1.在try块之前获取锁。 2.在try块中执行需要同步的代码。3.在finally块中释放锁,以确保锁一定会被释放,避免死锁。 下面是一个使用ReentrantLock的简单示例:```javaimportjava.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public classCounter {privatefinal Locklock =new ReentrantLock();privateint count=0;publicvoid increment() {lock.lock();//获取锁try {count++;} finally{lock.unlock();//确保释放锁}}publicint getCount(){lock.lock();try{return count;} finally{lock.unlock();}}} ```在这个例子中,我们使用ReentrantLock来保护对count变量的访问。注意,每次获取锁后,必须在finally块中释放锁。另外,Lock接口还提供了tryLock()方法,它可以用来避免死锁。例如:```javapublicvoid tryIncrement(){if (lock.tryLock()) {try{count++;}finally {lock.unlock();}} else{//执行其他操作,因为锁被其他线程持有} }``` Lock接口synchronized关键字相比,具有以下优势: -可以尝试获取锁而不必一直等待(tryLock())。-可以中断等待锁的线程lockInterruptibly())。-可以设置公平锁(ReentrantLock的构造函数可以指定公平策略,避免线程饥饿)。-可以绑定多个条件(Condition)。 但是,Lock接口需要手动释放锁,而synchronized在代码块结束时自动释放,因此使用Lock需要更加小心。关于Condition,它类似于Object的wait()和notify()方法,但提供了更灵活的功能。一个Lock可以创建多个Condition,用于实现更复杂的线程间通信。例如: ```javaLock lock= newReentrantLock(); Conditioncondition =lock.newCondition();//在某个线程lock.lock(); try{while (条件不满足) {condition.await(); //释放锁并等待}//执行操作} finally{lock.unlock();} //在另一个线程lock.lock();try {//改变条件condition.signalAll();//唤醒所有等待的线程} finally{lock.unlock();} ```总之,Lock接口提供了比synchronized更强大的锁操作,但使用也更为复杂。在需要高级功能(如可中断、超时、公平锁等)时,Lock接口是一个很好的选择。§§相关问题§§1. Lock接口synchronized关键字在性能上有何差异?2.什么是可重入锁?ReentrantLock是如何实现可重入的?3.如何实现公平锁和非公平锁?ReentrantLock的公平策略如何设置?4.Condition对象的使用场景有哪些?Object的wait/notify相比有什么优势?5.在使用Lock时,如何避免死锁?</think>### Java并发中的Lock接口使用指南 Java的`java.util.concurrent.locks.Lock`接口提供了比`synchronized`关键字更灵活、更强大的线程同步机制。以下是核心使用指南: #### 一、Lock接口核心方法 1. **lock()**:获取锁(阻塞直到获取) ```java Lock lock = new ReentrantLock(); lock.lock(); // 阻塞获取锁 try { // 临界区代码 } finally { lock.unlock(); // 必须在finally释放 } ``` 2. **tryLock()**:非阻塞尝试获取锁 ```java if (lock.tryLock()) { try { /* 操作共享资源 */ } finally { lock.unlock(); } } else { // 执行备选方案 } ``` 3. **tryLock(timeout, unit)**:带超时的尝试 ```java if (lock.tryLock(5, TimeUnit.SECONDS)) { try { /* 操作 */ } finally { unlock(); } } ``` 4. **lockInterruptibly()**:可中断的获取锁 ```java try { lock.lockInterruptibly(); try { /* 操作 */ } finally { unlock(); } } catch (InterruptedException e) { // 处理中断 } ``` #### 二、ReentrantLock(主要实现类) ```java ReentrantLock lock = new ReentrantLock(true); // true表示公平锁 ``` #### 三、Condition条件变量 ```java Condition condition = lock.newCondition(); // 等待条件 lock.lock(); try { while (!conditionSatisfied) { condition.await(); // 释放锁并等待 } } finally { unlock(); } // 通知条件变化 lock.lock(); try { condition.signalAll(); // 唤醒所有等待线程 } finally { unlock(); } ``` #### 四、最佳实践 1. **锁释放保证**:必须将`unlock()`放在`finally`块中 2. **避免嵌套锁**:容易导致死锁 3. **锁粒度控制**:尽量缩小临界区范围 4. **公平性选择**:公平锁减少线程饥饿,但性能较低 #### 五、synchronized对比优势 | 特性 | Lock接口 | synchronized | |--------------------|-----------------------------|--------------------| | 尝试非阻塞获取锁 | ✅ `tryLock()` | ❌ | | 可中断锁获取 | ✅ `lockInterruptibly()` | ❌ | | 超时获取锁 | ✅ `tryLock(timeout)` | ❌ | | 公平锁 | ✅ 可配置 | ❌ | | 多条件变量 | ✅ 多个Condition | ❌ (仅一个wait set) | > 示例:生产者消费者模型 ```java BlockingQueue<String> queue = new ArrayBlockingQueue<>(10); // 引用[2] Lock lock = new ReentrantLock(); Condition notFull = lock.newCondition(); Condition notEmpty = lock.newCondition(); // 生产者 lock.lock(); try { while (queue.remainingCapacity() == 0) notFull.await(); queue.put(item); notEmpty.signal(); } finally { lock.unlock(); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值