讀寫鎖的使用
本文內(nèi)容
何時該使用讀寫鎖.
讀寫鎖的寫法.
理解讀寫鎖和線程互斥的區(qū)別。
復(fù)習(xí)-同步化的概念
當一個方法或代碼塊被聲明成synchronized,要執(zhí)行此代碼必須先取得一個對象實例或this的鎖定,這個鎖定要在synchronized修飾的方法或代碼塊執(zhí)行完后才能釋放掉(無論這段代碼是怎樣返回的,是正常運行還是異常運行)。每個對象只有一個鎖定,如果有兩個不同的線程試圖同時調(diào)用同一對象的同步方法,最終只會有一個能運行此方法,另外一個要等待第一個線程釋放掉鎖定后才能運行此方法。
讀寫鎖應(yīng)用的場合
我們有時會遇到對同一個內(nèi)存區(qū)域如數(shù)組或者鏈表進行多線程讀寫的情況,一般來說有以下幾種處理方式: 1.不加任何限制,多見于讀取寫入都很快的情況,但有時也會出現(xiàn)問題. 2.對讀寫函數(shù)都加以同步互斥,這下問題是沒了,但效率也下去了,比如說兩個讀取線程不是非要排隊進入不可. 3.使用讀寫鎖,安全和效率都得到了解決,特別合適讀線程多于寫線程的情況.也就是下面將要展現(xiàn)的模式.
讀寫鎖的意圖
讀寫鎖的本意是分別對讀寫狀態(tài)進行互斥區(qū)分,有互斥時才加鎖,否則放行.互斥的情況有: 1.讀寫互斥. 2.寫寫互斥. 不互斥的情況是:讀讀,這種情況不該加以限制. 程序就是要讓鎖對象知道當前讀寫狀態(tài),再根據(jù)情況對讀寫的線程進行鎖定和解鎖。
讀寫線程都要操作的數(shù)據(jù)類
讀寫線程都要操作的數(shù)據(jù)是鏈表datas。
注意其中try...finally 的寫法,它保證了加鎖解鎖過程是成對調(diào)用的
private List<String> datas;
private ReadWriteLock lock;
public DataLib() {
datas = new ArrayList<String>();
lock = new ReadWriteLock();
}
// 寫入數(shù)據(jù),這時不能讀取
public void writeData(List<String> newDatas) {
try {
lock.writeLock();
Test.sleep(2);
datas=newDatas;
} finally {
lock.writeUnlock();
}
}
// 讀取數(shù)據(jù),這時不能寫入
public List<String> readData() {
try {
lock.readLock();
Test.sleep(1);
return datas;
} finally {
lock.readUnlock();
}
}
}
讀寫鎖ReadWriteLock類
public class ReadWriteLock{
// 讀狀態(tài)
private boolean isRead;
// 寫狀態(tài)
private boolean isWrite;
public synchronized void readLock(){
// 有寫入時讀取線程停止
while(isWrite){
try{
System.out.println("有線程在進行寫入,讀取線程停止,進入等待狀態(tài)");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
System.out.println("設(shè)定鎖為讀取狀態(tài)");
isRead=true;
}
public synchronized void readUnlock(){
System.out.println("解除讀取鎖");
isRead=false;
notifyAll();
}
public synchronized void writeLock(){
// 有讀取時讀取線程停止
while(isRead){
try{
System.out.println("有線程在進行讀取,寫入線程停止,進入等待狀態(tài)");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
// 有寫入時寫入線程也一樣要停止
while(isWrite){
try{
System.out.println("有線程在進行寫入,寫入線程停止,進入等待狀態(tài)");
wait();
}
catch(InterruptedException ex){
ex.printStackTrace();
}
}
System.out.println("設(shè)定鎖為寫入狀態(tài)");
isWrite=true;
}
public synchronized void writeUnlock(){
System.out.println("解除寫入鎖");
isWrite=false;
notifyAll();
}
}
寫線程類Writer -它用于往DataLib類實例中的datas字段寫數(shù)據(jù)
分析其中dataLib字段的用意。
注意并記住其中持續(xù)調(diào)用及使用隨機數(shù)的方法。
private DataLib dataLib;
private static final Random random=new Random();
private String[] mockDatas={"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"};
public Writer(DataLib dataLib,String[] mockDatas){
this.dataLib=dataLib;
this.mockDatas=mockDatas;
Thread thread=new Thread(this);
thread.start();
}
public void run(){
while(true){
Test.sleep(random.nextInt(3));
int startIndex=random.nextInt(mockDatas.length);
ArrayList<String> newDatas=new ArrayList<String>();
for(int i=startIndex;i<mockDatas.length;i++){
newDatas.add(mockDatas[i]);
}
dataLib.writeData(newDatas);
}
}
}
讀線程類Reader -它用于從DataLib類實例中的datas字段讀取數(shù)據(jù)
分析其中dataLib字段的用意。
注意并記住其中持續(xù)調(diào)用及使用隨機數(shù)的方法。
public class Reader implements Runnable{
private DataLib dataLib;
private static final Random random=new Random();
public Reader(DataLib dataLib){
this.dataLib=dataLib;
Thread thread=new Thread(this);
thread.start();
}
public void run(){
while(true){
Test.sleep(random.nextInt(2));
List<String> datas=dataLib.readData();
System.out.print(">>取得數(shù)組為:");
for(String data:datas){
System.out.print(data+",");
}
System.out.print("\n");
}
}
}
將代碼運行起來
右邊的代碼創(chuàng)建了兩個寫線程和三個讀線程,它們都是對dataLib實例進行操作的。
五個線程都有一個dataLib字段,都提供了一個帶參構(gòu)造函數(shù)以給datas字段賦值,這就保證了五個線程操作的都是一個實例的同一字段,也就是同一片內(nèi)存。
讀寫鎖就是對這五個線程進行控制的。
當有一個讀線程在操作時,其它的寫線程無法進行操作,讀線程可以正常操作,互不干擾。
當有一個寫線程在操作時,其它的讀線程無法進行操作。
public static void main(String[] args){
DataLib dataLib=new DataLib();
String[] mockDatas1={"甲","乙","丙","丁","戊","己","庚","辛","壬","癸"};
Writer writer1=new Writer(dataLib,mockDatas1);
String[] mockDatas2={"子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"};
Writer writer2=new Writer(dataLib,mockDatas2);
Reader reader1=new Reader(dataLib);
Reader reader2=new Reader(dataLib);
Reader reader3=new Reader(dataLib);
}
// 用于延時
public static void sleep(int sleepSecond){
try{
Thread.sleep(sleepSecond*1000);
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
小結(jié)
當多個線程試圖對同一內(nèi)容進行讀寫操作時適合使用讀寫鎖。
請理解并記住ReadWriteLock類讀寫鎖的寫法.
讀寫鎖相對于線程互斥的優(yōu)勢在于高效,它不會對兩個讀線程進行盲目的互斥處理,當讀線程數(shù)量多于寫線程尤其如此,當全是寫線程時兩者等效。
posted on 2008-02-22 14:28 和風細雨 閱讀(2615) 評論(2) 編輯 收藏 所屬分類: 線程