Jdk1.5以后,在java.util.concurrent.locks包下,有一組實現線程同步的接口和類,說到線程的同步,可能大家都會想到synchronized關鍵字,
這是java內置的關鍵字,用來處理線程同步的,但這個關鍵字有很多的缺陷,使用起來也不是很方便和直觀,所以就出現了Lock,下面,我們
就來對比着講解Lock。
通常我們在使用synchronized關鍵字的時候會遇到下面這些問題:
(1)不可控性,無法做到隨心的加鎖和釋放鎖。
(2)效率比較低下,比如我們現在並發的讀兩個文件,讀與讀之間是互不影響的,但如果給這個讀的對象使用synchronized來實現同步的話,
那么只要有一個線程進入了,那么其他的線程都要等待。
(3)無法知道線程是否獲取到了鎖。
而上面synchronized的這些問題,Lock都可以很好的解決,並且jdk1.5以后,還提供了各種鎖,例如讀寫鎖,但有一點需要注意,使用synchronized
關鍵時,無須手動釋放鎖,但使用Lock必須手動釋放鎖。下面我們就來學習一下Lock鎖。
Lock是一個上層的接口,其原型如下,總共提供了6個方法:
public interface Lock {
// 用來獲取鎖,如果鎖已經被其他線程獲取,則一直等待,直到獲取到鎖
void lock();
// 該方法獲取鎖時,可以響應中斷,比如現在有兩個線程,一個已經獲取到了鎖,另一個線程調用這個方法正在等待鎖,但是此刻又不想讓這個線程一直在這死等,可以通過
調用線程的Thread.interrupted()方法,來中斷線程的等待過程
void lockInterruptibly() throws InterruptedException;
// tryLock方法會返回bool值,該方法會嘗試着獲取鎖,如果獲取到鎖,就返回true,如果沒有獲取到鎖,就返回false,但是該方法會立刻返回,而不會一直等待
boolean tryLock();
// 這個方法和上面的tryLock差不多是一樣的,只是會嘗試指定的時間,如果在指定的時間內拿到了鎖,則會返回true,如果在指定的時間內沒有拿到鎖,則會返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 釋放鎖
void unlock();
// 實現線程通信,相當於wait和notify,后面會單獨講解
Condition newCondition();
}
那么這幾個方法該如何使用了?前面我們說到,使用Lock是需要手動釋放鎖的,但是如果程序中拋出了異常,那么就無法做到釋放鎖,有可能引起死鎖,
所以我們在使用Lock的時候,有一種固定的格式,如下:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {// 必須使用try,最后在finally里面釋放鎖
l.unlock();
}
下面我們來看一個簡單的例子,代碼如下:
/**
* 描述:Lock使用
*/
public class LockDemo {
// new一個鎖對象,注意此處必須聲明成類對象,保持只有一把鎖,ReentrantLock是Lock的唯一實現類
Lock lock = new ReentrantLock();
public void readFile(String fileMessage){
lock.lock();// 上鎖
try{
System.out.println(Thread.currentThread().getName()+"得到了鎖,正在讀取文件……");
for(int i=0; i<fileMessage.length(); i++){
System.out.print(fileMessage.charAt(i));
}
System.out.println();
System.out.println("文件讀取完畢!");
}finally{
System.out.println(Thread.currentThread().getName()+"釋放了鎖!");
lock.unlock();
}
}
public void demo(final String fileMessage){
// 創建若干個線程
ExecutorService service = Executors.newCachedThreadPool();
// 提交20個任務
for(int i=0; i<20; i++){
service.execute(new Runnable() {
@Override
public void run() {
readFile(fileMessage);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
// 釋放線程池中的線程
service.shutdown();
}
}