緊接着Callable和線程池,再次接觸java.util.concurrent併發包下的東西。Lock提供比synchronized更靈活的併發控制。Lock是java.util.concurrent.locks包下的接口,Lock 實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題。使用最多的Lock類是ReentrantLock。下面用它來實現上一篇日誌中的打印機的例子:
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- public class Printer {
- private Lock lock = new ReentrantLock();// 鎖對象
- public void printLetters(char c) {
- lock.lock();// 得到鎖
- try {
- for(int i = 0; i<5; i++) {
- System.out.print(c);
- }
- System.out.println();
- }finally {
- lock.unlock();// 釋放鎖
- }
- }
- }
這樣就實現了和sychronized一樣的同步效果,值得一提的是,用sychronized修飾的方法或者語句塊在代碼執行完之後鎖自動釋放,而是用Lock需要我們手動釋放鎖,所以爲了保證鎖最終被釋放(發生異常情況),要把互斥區放在try內,釋放鎖放在finally內。
讀鎖和寫鎖的使用:
以下內容轉載來至:http://blog.csdn.net/ghsau/article/details/7461369
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
如果說這就是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修飾符:
部分輸出結果:
- 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準備讀取數據