首先我們對讀寫鎖做一個概述:
假設你的程序中涉及到對一些共享資源的讀和寫操作,且寫操作沒有讀操作那么頻繁。在沒有寫操作的時候,兩個線程同時讀一個資源沒有任何問題,所以應該允許多個線程能在同時讀取共享資源。但是如果有一個線程想去寫這些共享資源,就不應該再有其它線程對該資源進行讀或寫,也就是說:讀-讀能共存,讀-寫不能共存,寫-寫不能共存。這就需要一個讀/寫鎖來解決這個問題。
阿里巴巴四面最后的問題就涉及到了讀寫鎖的實現。
問:對JAVA中的讀寫鎖熟悉嗎?
我:還可以...
問:簡單介紹一下...
我:這是JAVA中的集合類,ReentrantReadWriteLock,分別處理讀與寫的操作,讀讀可以共存,讀寫不能共存,寫寫不能共存...
問:你自己實現一個讀寫鎖
我:用synchronized,用一個隊列...
問:不能那么麻煩,就用兩個int值
我:兩個int值,那一定是一個標記讀次數,一個標記寫次數啊,於是想了一會我想出了以下答案:
問:你這個可以是可以,但是如果讀操作大量的話,寫操作會產生飢餓等待...如何改進?
我想了想,是哦,然后腦子就短路了,掙扎,再掙扎...
我:給寫加上優先級
問:不可以,就兩個int
我:讀次數設置一個閾值,超出主動釋放
問:不可以,就兩個int
我一臉懵逼...腦補畫面
問:提醒你一下,lockwrite那里改一下
面試官發音不標准,我聽了個lockread,想了半天,實在不知道怎么改,隨便說了一個
問:我讓你改lockwrite...
我:額,我看看...
又是漫長的等待...
我:沒想出來...
問:把你那個條件拆開,讓寫占個坑...
我恍然大悟,我去...
然后他大概說了一遍,就草草結束了,郁悶中...
應該改成:
回來之后,大概看了一下,把整個程序寫了一下:
package com.darrenchan.lock; /** * 用兩個int變量實現讀寫鎖 * @author Think * */ public class MyReadWriteLock { private int readcount = 0; private int writecount = 0; public void lockread() throws InterruptedException{ while(writecount > 0){ synchronized(this){ wait(); } } readcount++; //進行讀取操作 System.out.println("讀操作"); } public void unlockread(){ readcount--; synchronized(this){ notifyAll(); } } public void lockwrite() throws InterruptedException{ while(writecount > 0){ synchronized(this){ wait(); } } //之所以在這里先++,是先占一個坑,避免讀操作太多,從而產生寫的飢餓等待 writecount++; while(readcount > 0){ synchronized(this){ wait(); } } //進行寫入操作 System.out.println("寫操作"); } public void unlockwrite(){ writecount--; synchronized(this){ notifyAll(); } } public static void main(String[] args) throws InterruptedException { MyReadWriteLock readWriteLock = new MyReadWriteLock(); for(int i = 0; i < 2; i++){ Thread2 thread2 = new Thread2(i, readWriteLock); thread2.start(); } for (int i = 0; i < 10; i++) { Thread1 thread1 = new Thread1(i, readWriteLock); thread1.start(); } } } class Thread1 extends Thread{ public int i; public MyReadWriteLock readWriteLock; public Thread1(int i, MyReadWriteLock readWriteLock) { this.i = i; this.readWriteLock = readWriteLock; } @Override public void run() { try { readWriteLock.lockread(); Thread.sleep(1000);//模擬耗時 System.out.println("第"+i+"個讀任務"); } catch (InterruptedException e) { e.printStackTrace(); } finally { readWriteLock.unlockread(); } } } class Thread2 extends Thread{ public int i; public MyReadWriteLock readWriteLock; public Thread2(int i, MyReadWriteLock readWriteLock) { this.i = i; this.readWriteLock = readWriteLock; } @Override public void run() { try { readWriteLock.lockwrite(); Thread.sleep(1000); System.out.println("第"+i+"個寫任務"); } catch (InterruptedException e) { e.printStackTrace(); } finally { readWriteLock.unlockwrite(); } } }
執行結果:
寫操作
第0個寫任務
讀操作
讀操作
讀操作
讀操作
讀操作
讀操作
讀操作
讀操作
讀操作
讀操作
第9個讀任務
第3個讀任務
第1個讀任務
第8個讀任務
第2個讀任務
第0個讀任務
第7個讀任務
第4個讀任務
第5個讀任務
第6個讀任務
寫操作
第1個寫任務
當然這是簡單的,還可以進一步加深,可以參考博客:http://ifeve.com/read-write-locks/#simple
注:以上代碼在++和--的時候仍然會產生並發異常,建議用AtomicInteger類型,在硬件上保證++和--操作不會出現並發異常。