Java中的鎖框架指的是java.util.concurrent.locks這個包裏的,不同於對象的內置加鎖同步以及java.lang.Object的等待/通知機制,包含鎖框架的併發工具通過輪詢鎖、限時等待及其他方式改善了這種機制。
鎖
這裏的鎖指的是接口Lock
方法
包含了如下幾種方法:
void lock():獲取鎖。當鎖不可用時,調用線程會被強制一直等待直到鎖可用。
void lockInterruptibly():除非調用線程被中斷,否則獲得鎖。當鎖不可用時,調用線程被強制一直等待,直到鎖可用或線程中斷。
Condition newCondition():返回一個新的綁定到當前鎖示例之上的Condition實例。當Lock的實現類不支持條件時,拋出異常。
boolean tryLock():在該方法被調用時,鎖可用則獲得鎖,當獲得鎖之後,該方法返回true;反之返回false。
boolean tryLock(long time, Time unit):當在制定等待時間內可用鎖可用並且調用線程沒被中斷則獲得鎖,等待時間以java.util.concurrent.TimeUnit計(秒、毫秒等等)。當鎖不可用時,調用線程會在指定時間內一直等待,直到鎖可用或線程中斷。中斷拋出異常。
void unlock():釋放鎖
使用方式
一般使用鎖我們需要用到下面這樣的形式:
Lock lock = ...;// 實例化接口
lock.lock();
try {
//使用鎖獲取到的資源
} catch (Exception e) {
//異常處理
} finally {
lock.unlock();
}
重入鎖
類ReentranLock實現了接口Lock,這是一個可重入的互斥鎖,這個鎖是和一個持有量相關聯的。當一條線程持有這個鎖並且調用lock()、lockUnitinterruptibly()或者任意一個tryLock()方法重新獲取鎖,這個持有量就遞增1。當線程調用unlock()方法,持有量就遞減1。當持有量爲0時,鎖就會被釋放。
方法
ReentranLock中的方法其實就是實現了Lock接口中的方法,這裏需要說的是它的兩個構造方法:
ReentrantLock():創建一個ReentrantLock的實例,該構造函數等價於ReentrantLock(false)。
ReentrantLock(boolen fair):創建一個擁有指定公平策略的實例。當這個鎖需要使用公平的 排序策略 時,給fair複製爲true。 在爭用的環境下,這個鎖傾向於將訪問權限分配給 等待最久的線程 。
還有兩個ReentranLock自己的方法:
boolean isFair():返回是否有公平策略。
boolean isHeldByCurrentThread():返回鎖是否被當前線程持有。
注意,ReentranLock如果在沒有持有鎖的情況下調用unlock()會拋出IllegalMonitorStateException異常。
使用方法
示範如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class RLDemo {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
final ReentrantLock lock = new ReentrantLock();
class Worker implements Runnable {
private final String name;
Worker(String name) {
this.name = name;
}
@Override
public void run() {
lock.lock();
try {
if (lock.isHeldByCurrentThread()) {
System.out.printf("Thread %s entered critical section.%n", name);
}
System.out.printf("Thread %s performing work.%n", name);
try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
System.out.printf("Thread %s finish work.%n", name);
}finally {
lock.unlock();
}
}
}
executorService.execute(new Worker("ThreadA"));
executorService.execute(new Worker("ThreadB"));
try {
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdownNow();
}
}
結果爲:
可以看到,兩條線程並不是交替執行的,當一個線程調用lock()方法而鎖又不可用時,這個線程就一直禁用,直到鎖可用(即鎖被持有鎖的進程釋放)。
條件
接口Condition把Object的wait和notification方法(wait(),notify()和notifyAll())分解到不同的條件對象中。通過把這些條件和任意Lock實現的使用組合起來,起到讓每個對象具有多重等待集合的作用。這裏Lock取代了同步方法、代碼塊,Condition取代了Object的wait、notification方法。
一個Condition的實例原本就會綁定到一個鎖上。可以使用Lock的newCondition()方法去獲取一個針對特定Lock實例的Condition實例。
方法
Condition聲明瞭下列方法:
void await():在接收到信號或者被中斷之前,強制調用線程一直等待。
boolean await(long time, TimeUnit unit):在接收到信號、被中斷,或者超過指定的等待時間之前,強制調用線程一直等待。
long awaitNanos(long nanosTimeout):同上。返回值爲等待結果時間的估算
void awaitUninterruptibly():在接收到信號之前,強制當前線程一直等待。
boolean awaitUntil(Date deadline):同上上上。
void signal():喚醒一個等待中的線程。
void signalAll():喚醒所有等待中的線程。
使用方法
- 共享變量
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Shared {
private char c;
private volatile boolean available;
private final Lock lock;
private final Condition condition;
Shared() {
available = false;
lock = new ReentrantLock();
condition = lock.newCondition();
}
Lock getLock() {
return lock;
}
char getSharedChar() {
lock.lock();
try {
while (!available) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;
condition.signal();
} finally {
lock.unlock();
return c;
}
}
void setSharedChar(char c) {
lock.lock();
try {
while (available) {
try {
condition.await();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
this.c = c;
available = true;
condition.signal();
}finally {
lock.unlock();
}
}
}
- 生產者
import java.util.concurrent.locks.Lock;
public class Producer extends Thread{
private final Lock lock;
private final Shared shard;
Producer(Shared shard) {
this.shard = shard;
this.lock = shard.getLock();
}
@Override
public void run() {
for (char ch = 'A'; ch <= 'D'; ch++) {
lock.lock();
shard.setSharedChar(ch);
System.out.println(ch + " produced by producer");
lock.unlock();
}
}
}
- 消費者
import java.util.concurrent.locks.Lock;
public class Consumer extends Thread {
private final Lock lock;
private final Shared shard;
Consumer(Shared shard) {
this.shard = shard;
lock = shard.getLock();
}
@Override
public void run() {
char c;
do {
lock.lock();
c = shard.getSharedChar();
System.out.println(c + " consumed by consumer.");
lock.unlock();
} while (c != 'D');
}
}
- 調用
public class PC {
public static void main(String[] args) {
Shared shared = new Shared();
new Producer(shared).start();
new Consumer(shared).start();
}
}
結果如下:
可以看到,生產者和消費者獲取到的鎖均爲共享變量當中的,而共享變量內部則使用條件變量來控制