去故就新 Java線程新同步機制
1、可重入鎖ReentrantLock,相當于synchronized塊,為臨界區提供互斥訪問機制。
(1) 相關的接口
創建一個可重入鎖
- Lock lock = new ReentrantLock();
請求鎖,如果鎖被當前另一個線程持有,則阻塞。
- void lock();
釋放鎖
- void unlock();
非阻塞型lock()
- boolean tryLock();
(2) 使用基本結構
- locker.lock();
- try{
- //code here to access the cirtical section
- }finally{
- locker.unlock();
- }
這種結構保證在任何時刻只有一個線程能夠進入臨界區,如果一個線程鎖住了鎖對象,其他任何線程在調用lock時,都會被阻塞,直到第一個線程釋放鎖對象。而且無論try塊是否拋出異常,都會執行finally block,解鎖locker。
(3) 鎖的可重入性
鎖是可重入的,線程能夠重復地獲取它已經擁有的鎖。鎖對象維護一個持有計數(hold count)來追蹤對lock方法的嵌套調用。線程在每次調用lock后都要調用unlock來釋放鎖。由于這個特性,被一個鎖保護的代碼可以調用另一個使用相同鎖的方法。
(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,相當于wait-notify機制,提供一種線程間的等待通知機制,condition中的等待-通知方法是await(),signal(),signalAll(),也需要在互斥環境下被調用。
(1) 相關的接口
創建Condition對象,Condition對象是跟Lock關聯在一起的。
- Lock locker = new ReentrantLock();
- Condition cond = locker.newCondition();
把此線程放到條件的等待集中。
- void await();
解除此條件的等待集中所有線程的阻塞狀態。
- void signalAll();
在此條件的等待集中隨機選擇一個線程,解除其阻塞狀態。
- void signal();
(2) 使用的基本結構:
- //初始時ok_to_proceed為false.
- locker.lock()
- try{
- while(!ok_to_proceed){
- //進入等待此條件集中,被阻塞,它維持狀態直到另一個線程調用同一個條件上的。
- //signalAll/signal方法時為止。
- cond.await();
- }
- }finally{
- cker.unlock();
- }
- locker.lock();
- try{
- //調用將解除所有等待此條件下的線程的阻塞狀態。當線程從等待集中被移走時,它們將再次成為可運行的,調度器將再次激活它們
- //此時,它們將試圖重新進入對象。一旦鎖可獲得,它們中的某個線程將從await調用返回,從而獲得鎖并從它被阻塞的地方繼續執行。
- ok_to_proceed = true;
- cond.signalAll() or cond.signal();
- }finally{
- locker.unlock();
- }
ok_to_proceed也是為了防止wait-notify出現的問題,即再wait之間,notify()已經給出通知,此時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,適用于"讀多寫少"的多線程應用場景,"讀-寫"互斥,"寫-寫"互斥,而讀-讀可以共享同讀鎖,即一個線程獲取讀鎖,其它線程可直接進入讀,不會被阻塞。
(1) 相關接口
創建讀寫鎖對象
- ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
獲取讀鎖
- Lock readLock = rwLock.readLock();
獲取寫鎖
- Lock writeLock = rwLock.writeLock();
(2).讀寫鎖使用基本結構
- //對所有的讀操作添加讀鎖
- 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、總結
Lock接口替代synchronized
Lock接口可以比sychronized提供更廣泛的鎖定操作,可以提供多把不同的鎖,且鎖之間互不干涉。
Lock接口提供lock()與unlock()方法,使用明確調用來完成同步的,OO思想好于前者。
Lock可以自由操控同步范圍(scope)。
Lock接口支持nested lock(嵌套鎖定),并提供了豐富的api。
Lock接口提供了tryLock()方法,支持嘗試取得某個object lock。
原文鏈接:http://yuyiming1986.iteye.com/blog/1272979
【編輯推薦】