java-jdk1.5新增的併發包內容
1.各種同步控制工具的使用:
1.1 ReentrantLock
1.1.1 可重入
ReentrantLock和Synchronized比較:
ReentrantLock:需要在finally進行釋放鎖,且可以重入,即可以反覆得到相同的一把鎖,它有一個與鎖相關的計數器,如果擁有鎖的某個執行緒再次得到這把鎖,這個計數器將會+1,然後鎖需要釋放兩次才需要真正釋放。
Synchronized:釋放鎖是由JVM實現的。當這把鎖被一個執行緒所佔據,其他執行緒只能等待這把鎖被釋放,才可以進入。
相比之下,ReentrantLock比Synchronized效能更好,如果只是簡單的實現,建議使用Synchronized,不要刻意使用ReentrantLock。
下圖是使用Synchronized的程式碼及效果展示:
package test; public class TestTest1 implements Runnable { public synchronized void method2() { System.out.println("method2=" + Thread.currentThread().getId()); method1(); } public synchronized void method1() { System.out.println("method1=" + Thread.currentThread().getId()); } @Override public void run() { method2(); } public static void main(String[] args) { TestTest1 test = new TestTest1(); Thread thread1=new Thread(test); thread1.start(); Thread thread2=new Thread(test); thread2.start(); Thread thread3=new Thread(test); thread3.start(); } }
輸出如下圖所示:
使用ReentrantLock的程式碼及效果展示:
package test; import java.util.concurrent.locks.ReentrantLock; public class TestTest2 implements Runnable { ReentrantLock lock = new ReentrantLock(); public void get() { lock.lock(); System.out.println("get=" + Thread.currentThread().getId()); set(); //lock.unlock(); } public void set() { lock.lock(); System.out.println("set=" + Thread.currentThread().getId()); lock.unlock(); } @Override public void run() { get(); } public static void main(String[] args) { TestTest2 test = new TestTest2(); Thread thread1 = new Thread(test); thread1.start(); Thread thread2 = new Thread(test); thread2.start(); Thread thread3 = new Thread(test); thread3.start(); } }
從結果可以看出,使用ReentrantLock,需要手動釋放鎖,否則就會等待。
將程式碼註釋去掉,就等同於Synchronized,結果如下:
也可以在finally塊中加入lock.unlock();
1.1.2 可中斷
ReentrantLock中的lock.lockInterruptibly(),可以捕捉到中斷的異常,而lock.lock()則不能,示例如下:
package test;
import java.util.concurrent.locks.ReentrantLock;
public class TestTest3 implements Runnable {
static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();
// try {
// lock.lockInterruptibly();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + " interrupted");
}
public static void main(String[] args) {
lock = new ReentrantLock();
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
TestTest3 test = new TestTest3();
try {
Thread thread1 = new Thread(test);
thread1.start();
Thread.sleep(1000);
thread1.interrupt();
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果顯示沒有報異常,只是一直在休眠。
而把lock.lock()註釋掉,使用lock.lockInterruptibly(),可以捕捉到異常,如下圖所示:
這就證明了其可中斷性。
總結:
1. 執行緒A和B都要獲取物件O的鎖定,假設A獲取了物件O鎖,B將等待A釋放對O的鎖定,
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹別的事情
2.ReentrantLock獲取鎖定與三種方式:
a) lock(), 如果獲取了鎖立即返回,如果別的執行緒持有鎖,當前執行緒則一直處於休眠狀態,直到獲取鎖
b) tryLock(), 如果獲取了鎖立即返回true,如果別的執行緒正持有鎖,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的執行緒正持有鎖,會等待引數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;
d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前執行緒處於休眠狀態,直到或者鎖定,或者當前執行緒被別的執行緒中斷
3.synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在程式碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過程式碼實現的,要保證鎖定一定會被釋放,就必須將unLock()放到finally{}中