去故就新 Java線程新同步機制
1、可重入鎖ReentrantLock,相當(dāng)于synchronized塊,為臨界區(qū)提供互斥訪問機制。
(1) 相關(guān)的接口
創(chuàng)建一個可重入鎖
- Lock lock = new ReentrantLock();
請求鎖,如果鎖被當(dāng)前另一個線程持有,則阻塞。
- void lock();
釋放鎖
- void unlock();
非阻塞型lock()
- boolean tryLock();
(2) 使用基本結(jié)構(gòu)
- locker.lock();
- try{
- //code here to access the cirtical section
- }finally{
- locker.unlock();
- }
這種結(jié)構(gòu)保證在任何時刻只有一個線程能夠進(jìn)入臨界區(qū),如果一個線程鎖住了鎖對象,其他任何線程在調(diào)用lock時,都會被阻塞,直到第一個線程釋放鎖對象。而且無論try塊是否拋出異常,都會執(zhí)行finally block,解鎖locker。
(3) 鎖的可重入性
鎖是可重入的,線程能夠重復(fù)地獲取它已經(jīng)擁有的鎖。鎖對象維護(hù)一個持有計數(shù)(hold count)來追蹤對lock方法的嵌套調(diào)用。線程在每次調(diào)用lock后都要調(diào)用unlock來釋放鎖。由于這個特性,被一個鎖保護(hù)的代碼可以調(diào)用另一個使用相同鎖的方法。
(4) 示例代碼:
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- class WorkerOne extends Thread{
- private Lock locker;
- public WorkerOne (Lock locker){
- this.locker = locker;
- }
- public void run(){
- locker.lock();
- try{
- System.out.println(Thread.currentThread().getName()+":step into critical section");
- }finally{
- locker.unlock();
- }
- }
- }
- class WorkerTwo extends Thread{
- private Lock locker;
- public WorkerTwo (Lock locker){
- this.locker = locker;
- }
- public void sayHello(){
- locker.lock();
- try{ System.out.println(Thread.currentThread().getName()+":call sayHello()");
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }finally{
- locker.unlock();
- }
- }
- public void run(){
- locker.lock();
- try{ System.out.println(Thread.currentThread().getName()+":setp into critical section");
- //測試鎖的可重入性
- sayHello();
- }finally{
- locker.unlock();
- }
- }
- }
- public class Test5 {
- public static void main(String[] args) {
- Lock locker = new ReentrantLock();
- WorkerOne wo= new WorkerOne(locker);
- wo.setName("WorkerOne");
- WorkerTwo wt = new WorkerTwo(locker);
- wt.setName("WorkerTwo");
- wt.start();
- wo.start();
- }
- }
輸出:
WorkerTwo:setp into critical section WorkerTwo:call sayHello() WorkerOne:step into critical section
2、條件對象Condition,相當(dāng)于wait-notify機制,提供一種線程間的等待通知機制,condition中的等待-通知方法是await(),signal(),signalAll(),也需要在互斥環(huán)境下被調(diào)用。
(1) 相關(guān)的接口
創(chuàng)建Condition對象,Condition對象是跟Lock關(guān)聯(lián)在一起的。
- Lock locker = new ReentrantLock();
- Condition cond = locker.newCondition();
把此線程放到條件的等待集中。
- void await();
解除此條件的等待集中所有線程的阻塞狀態(tài)。
- void signalAll();
在此條件的等待集中隨機選擇一個線程,解除其阻塞狀態(tài)。
- void signal();
(2) 使用的基本結(jié)構(gòu):
- //初始時ok_to_proceed為false.
- locker.lock()
- try{
- while(!ok_to_proceed){
- //進(jìn)入等待此條件集中,被阻塞,它維持狀態(tài)直到另一個線程調(diào)用同一個條件上的。
- //signalAll/signal方法時為止。
- cond.await();
- }
- }finally{
- cker.unlock();
- }
- locker.lock();
- try{
- //調(diào)用將解除所有等待此條件下的線程的阻塞狀態(tài)。當(dāng)線程從等待集中被移走時,它們將再次成為可運行的,調(diào)度器將再次激活它們
- //此時,它們將試圖重新進(jìn)入對象。一旦鎖可獲得,它們中的某個線程將從await調(diào)用返回,從而獲得鎖并從它被阻塞的地方繼續(xù)執(zhí)行。
- ok_to_proceed = true;
- cond.signalAll() or cond.signal();
- }finally{
- locker.unlock();
- }
ok_to_proceed也是為了防止wait-notify出現(xiàn)的問題,即再wait之間,notify()已經(jīng)給出通知,此時wait只會一直等待下去,這樣就保證了signal()線程的通知被await()線程接收到。
(3) 測試代碼:
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- class GlobalV{
- public final static Lock locker = new ReentrantLock();
- public final static Condition cond = locker.newCondition();
- public static boolean to_proceed = false;
- }
- class Response extends Thread{
- public void run(){
- while(true){
- GlobalV.locker.lock();
- try{
- while(!GlobalV.to_proceed){
- GlobalV.cond.await();
- }
- System.out.println("Response:finish a job");
- GlobalV.to_proceed = false;
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- GlobalV.locker.unlock();
- }
- }
- }
- }
- class Request extends Thread{
- public void run(){
- while(true){
- GlobalV.locker.lock();
- try{
- GlobalV.to_proceed = true;
- GlobalV.cond.signalAll();
- System.out.println("Request:send a job to Response");
- }finally{
- GlobalV.locker.unlock();
- }
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public class Test6 {
- public static void main(String[] args) {
- Request req = new Request();
- Response res = new Response();
- req.start();
- res.start();
- }
- }
輸出:
Request:send a job to Response Response:finish a job Request:send a job to Response Response:finish a job Request:send a job to Response Response:finish a job Request:send a job to Response Response:finish a job
#p#
3、讀寫鎖ReentrantReadWriteLock,適用于"讀多寫少"的多線程應(yīng)用場景,"讀-寫"互斥,"寫-寫"互斥,而讀-讀可以共享同讀鎖,即一個線程獲取讀鎖,其它線程可直接進(jìn)入讀,不會被阻塞。
(1) 相關(guān)接口
創(chuàng)建讀寫鎖對象
- ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
獲取讀鎖
- Lock readLock = rwLock.readLock();
獲取寫鎖
- Lock writeLock = rwLock.writeLock();
(2).讀寫鎖使用基本結(jié)構(gòu)
- //對所有的讀操作添加讀鎖
- readLock.lock();
- try{
- //code to read
- }finally{
- readLock.unlock();
- }
- //對所有的寫操作添加寫鎖
- writeLock.lock();
- try{
- //code to write
- }finally{
- writeLock.unlock();
- }
(3) 測試代碼:
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- class Reader extends Thread {
- private Lock readLock = null;
- public Reader(Lock readLock) {
- this.readLock = readLock;
- }
- public void run() {
- while (true) {
- readLock.lock();
- try {
- System.out.println(Thread.currentThread().getName()
- + ":read action for 1 seconds-"+ReadWriteLock.testVal);
- } finally {
- readLock.unlock();
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class Writer extends Thread {
- private Lock writeLock = null;
- public Writer(Lock writeLock) {
- this.writeLock = writeLock;
- }
- public void run() {
- while (true) {
- writeLock.lock();
- try {
- System.out.println(Thread.currentThread().getName()
- + ":write action for 2 seconds");
- if(ReadWriteLock.testVal.equals("1111"))
- ReadWriteLock.testVal = "2222";
- else
- ReadWriteLock.testVal = "1111";
- } finally {
- writeLock.unlock();
- }
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public class ReadWriteLock {
- public static String testVal = "Initiation";
- public static void main(String[] args) {
- ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
- Lock readLock = lock.readLock();
- Lock writeLock = lock.writeLock();
- Reader reader1 = new Reader(readLock);
- reader1.setName("reader1");
- Reader reader2 = new Reader(readLock);
- reader2.setName("reader2");
- Reader reader3 = new Reader(readLock);
- reader3.setName("reader3");
- Reader reader4 = new Reader(readLock);
- reader4.setName("reader4");
- Writer writer = new Writer(writeLock);
- writer.setName("writer1");
- reader1.start();
- reader2.start();
- reader3.start();
- reader4.start();
- writer.start();
- }
- }
輸出:
reader1:read action for 1 seconds-Initiation reader3:read action for 1 seconds-Initiation writer1:write action for 2 seconds reader2:read action for 1 seconds-1111 reader4:read action for 1 seconds-1111 reader3:read action for 1 seconds-1111 reader1:read action for 1 seconds-1111 reader4:read action for 1 seconds-1111 reader2:read action for 1 seconds-1111 writer1:write action for 2 seconds reader4:read action for 1 seconds-2222 reader1:read action for 1 seconds-2222 reader3:read action for 1 seconds-2222 reader2:read action for 1 seconds-2222
4、總結(jié)
Lock接口替代synchronized
Lock接口可以比sychronized提供更廣泛的鎖定操作,可以提供多把不同的鎖,且鎖之間互不干涉。
Lock接口提供lock()與unlock()方法,使用明確調(diào)用來完成同步的,OO思想好于前者。
Lock可以自由操控同步范圍(scope)。
Lock接口支持nested lock(嵌套鎖定),并提供了豐富的api。
Lock接口提供了tryLock()方法,支持嘗試取得某個object lock。
原文鏈接:http://yuyiming1986.iteye.com/blog/1272979
【編輯推薦】