# Java線程(八):鎖對象Lock-同步問題更完美的處理方式
Lock是[java.util.concurrent.locks](http://blog.csdn.net/ghsau/article/details/7461369)包下的接口,Lock?實現提供了比使用synchronized?方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題,我們拿[Java線程(二)](http://blog.csdn.net/ghsau/article/details/7424694)中的一個例子簡單的實現一下和sychronized一樣的效果,代碼如下:
~~~
public?class?LockTest?{??
????public?static?void?main(String[]?args)?{??
????????final?Outputter1?output?=?new?Outputter1();??
????????new?Thread()?{??
????????????public?void?run()?{??
????????????????output.output("zhangsan");??
????????????};??
????????}.start();????????
????????new?Thread()?{??
????????????public?void?run()?{??
????????????????output.output("lisi");??
????????????};??
????????}.start();??
????}??
}??
class?Outputter1?{??
????private?Lock?lock?=?new?ReentrantLock();//?鎖對象??
????public?void?output(String?name)?{??
????????//?TODO?線程輸出方法??
????????lock.lock();//?得到鎖??
????????try?{??
????????????for(int?i?=?0;?i?
????????????????System.out.print(name.charAt(i));??
????????????}??
????????}?finally?{??
????????????lock.unlock();//?釋放鎖??
????????}??
????}??
}??
~~~
這樣就實現了和sychronized一樣的同步效果,需要注意的是,用sychronized修飾的方法或者語句塊在代碼執行完之后鎖自動釋放,而用Lock需要我們手動釋放鎖,所以為了保證鎖最終被釋放(發生異常情況),要把互斥區放在try內,釋放鎖放在finally內。
如果說這就是Lock,那么它不能成為同步問題更完美的處理方式,下面要介紹的是讀寫鎖(ReadWriteLock),我們會有一種需求,在對數據進行讀寫的時候,為了保證數據的一致性和完整性,需要讀和寫是互斥的,寫和寫是互斥的,但是讀和讀是不需要互斥的,這樣讀和讀不互斥性能更高些,來看一下不考慮互斥情況的代碼原型:
~~~
public?class?ReadWriteLockTest?{??
????public?static?void?main(String[]?args)?{??
????????final?Data?data?=?new?Data();??
????????for?(int?i?=?0;?i?3;?i++)?{??
????????????new?Thread(new?Runnable()?{??
????????????????public?void?run()?{??
????????????????????for?(int?j?=?0;?j?5;?j++)?{??
????????????????????????data.set(new?Random().nextInt(30));??
????????????????????}??
????????????????}??
????????????}).start();??
????????}?????????
????????for?(int?i?=?0;?i?3;?i++)?{??
????????????new?Thread(new?Runnable()?{??
????????????????public?void?run()?{??
????????????????????for?(int?j?=?0;?j?5;?j++)?{??
????????????????????????data.get();??
????????????????????}??
????????????????}??
????????????}).start();??
????????}??
????}??
}??
class?Data?{??????
????private?int?data;//?共享數據??????
????public?void?set(int?data)?{??
????????System.out.println(Thread.currentThread().getName()?+?"準備寫入數據");??
????????try?{??
????????????Thread.sleep(20);??
????????}?catch?(InterruptedException?e)?{??
????????????e.printStackTrace();??
????????}??
????????this.data?=?data;??
????????System.out.println(Thread.currentThread().getName()?+?"寫入"?+?this.data);??
????}?????
????public?void?get()?{??
????????System.out.println(Thread.currentThread().getName()?+?"準備讀取數據");??
????????try?{??
????????????Thread.sleep(20);??
????????}?catch?(InterruptedException?e)?{??
????????????e.printStackTrace();??
????????}??
????????System.out.println(Thread.currentThread().getName()?+?"讀取"?+?this.data);??
????}??
}??
~~~
部分輸出結果:
~~~
Thread-1準備寫入數據??
Thread-3準備讀取數據??
Thread-2準備寫入數據??
Thread-0準備寫入數據??
Thread-4準備讀取數據??
Thread-5準備讀取數據??
Thread-2寫入12??
Thread-4讀取12??
Thread-5讀取5??
Thread-1寫入12??
~~~
我們要實現寫入和寫入互斥,讀取和寫入互斥,讀取和讀取互斥,在set和get方法加入sychronized修飾符:
~~~
public?synchronized?void?set(int?data)?{...}??????
public?synchronized?void?get()?{...}??
~~~
部分輸出結果:
~~~
Thread-0準備寫入數據??
Thread-0寫入9??
Thread-5準備讀取數據??
Thread-5讀取9??
Thread-5準備讀取數據??
Thread-5讀取9??
Thread-5準備讀取數據??
Thread-5讀取9??
Thread-5準備讀取數據??
Thread-5讀取9??
~~~
我們發現,雖然寫入和寫入互斥了,讀取和寫入也互斥了,但是讀取和讀取之間也互斥了,不能并發執行,效率較低,用讀寫鎖實現代碼如下:
~~~
class?Data?{??????
????private?int?data;//?共享數據??
????private?ReadWriteLock?rwl?=?new?ReentrantReadWriteLock();?????
????public?void?set(int?data)?{??
????????rwl.writeLock().lock();//?取到寫鎖??
????????try?{??
????????????System.out.println(Thread.currentThread().getName()?+?"準備寫入數據");??
????????????try?{??
????????????????Thread.sleep(20);??
????????????}?catch?(InterruptedException?e)?{??
????????????????e.printStackTrace();??
????????????}??
????????????this.data?=?data;??
????????????System.out.println(Thread.currentThread().getName()?+?"寫入"?+?this.data);??
????????}?finally?{??
????????????rwl.writeLock().unlock();//?釋放寫鎖??
????????}??
????}?????
????public?void?get()?{??
????????rwl.readLock().lock();//?取到讀鎖??
????????try?{??
????????????System.out.println(Thread.currentThread().getName()?+?"準備讀取數據");??
????????????try?{??
????????????????Thread.sleep(20);??
????????????}?catch?(InterruptedException?e)?{??
????????????????e.printStackTrace();??
????????????}??
????????????System.out.println(Thread.currentThread().getName()?+?"讀取"?+?this.data);??
????????}?finally?{??
????????????rwl.readLock().unlock();//?釋放讀鎖??
????????}??
????}??
}??
~~~
部分輸出結果:
~~~
Thread-4準備讀取數據??
Thread-3準備讀取數據??
Thread-5準備讀取數據??
Thread-5讀取18??
Thread-4讀取18??
Thread-3讀取18??
Thread-2準備寫入數據??
Thread-2寫入6??
Thread-2準備寫入數據??
Thread-2寫入10??
Thread-1準備寫入數據??
Thread-1寫入22??
Thread-5準備讀取數據??
~~~
從結果可以看出實現了我們的需求,這只是鎖的基本用法,鎖的機制還需要繼續深入學習。
- 前言
- Java線程(一):線程安全與不安全
- Java線程(二):線程同步synchronized和volatile
- Java線程(三):線程協作-生產者/消費者問題
- Java線程(四):線程中斷、線程讓步、線程睡眠、線程合并
- Java線程(五):Timer和TimerTask
- Java線程(六):線程池
- Java線程(七):Callable和Future
- Java線程(八):鎖對象Lock-同步問題更完美的處理方式
- Java線程(九):Condition-線程通信更高效的方式
- Java線程(十):CAS
- Java線程(十一):Fork/Join-Java并行計算框架
- Java線程(篇外篇):阻塞隊列BlockingQueue
- Java線程(篇外篇):線程本地變量ThreadLocal
- Java線程(篇外篇):線程和鎖