對象的方法中一旦加入synchronized修飾,則任何時刻只能有一個線程訪問synchronized修飾的方法。假設有個數據對象擁有寫方法與讀方法,多線程環境中要想保證數據的安全,需對該對象的讀寫方法都要加入 synchronized同步塊。這樣任何線程在寫入時,其它線程無法讀取與改變數據;如果有線程在讀取時,其他線程也無法讀取或寫入。這種方式在寫入操作遠大於讀操作時,問題不大,而當讀取遠遠大於寫入時,會造成性能瓶頸,因爲此種情況下讀取操作是可以同時進行的,而加鎖操作限制了數據的併發讀取。 ReadWriteLock解決了這個問題,當寫操作時,其他線程無法讀取或寫入數據,而當讀操作時,其它線程無法寫入數據,但卻可以讀取數據 。
且看 以下例子
public class ReadWriteLockDemo {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
Data data = new Data();
Worker t1 = new Worker(data,true);
Worker t2 = new Worker(data,true);
t1.start();
t2.start();
}
static class Worker extends Thread {
Data data;
boolean read;
public Worker(Data data, boolean read) {
this.data = data;
this.read = read;
}
public void run() {
if (read)
data.get();
else
data.set();
}
}
static class Data {
ReadWriteLock lock = new ReentrantReadWriteLock();
Lock read = lock.readLock();
Lock write = lock.writeLock();
public void set() {
write.lock();
System.out.println(Thread.currentThread().hashCode()
+ " set:begin " + sdf.format(new Date()));
try {
Thread.sleep(5000);
//
} catch (Exception e) {
} finally {
System.out.println(Thread.currentThread().hashCode() + " set:end "
+ sdf.format(new Date()));
write.unlock();
}
}
public int get() {
read.lock();
System.out.println(Thread.currentThread().hashCode()
+ " get :begin " + sdf.format(new Date()));
try {
Thread.sleep(5000);
//
} catch (Exception e) {
} finally {
System.out.println(Thread.currentThread().hashCode() + " get :end "
+ sdf.format(new Date()));
read.unlock();
}
return 1;
}
}
}
兩個線程均是讀線程,結果如下
22474382 get :begin 2011-04-16 18:26:13
4699264 get :begin 2011-04-16 18:26:13
22474382 get :end 2011-04-16 18:26:18
4699264 get :end 2011-04-16 18:26:18
兩讀線程均可同時讀取數據,下面看一個是讀線程,一個寫線程的情況
Data data = new Data();
Worker t1 = new Worker(data,false);
Worker t2 = new Worker(data,true);
t2.start();
Thread.sleep(100);
t1.start();
先啓動讀取線程,再啓動寫入線程,看結果
14718739 get :begin 2011-04-16 18:54:46
14718739 get :end 2011-04-16 18:54:51
14737862 set:begin 2011-04-16 18:54:51
14737862 set:end 2011-04-16 18:54:56
可以看到讀取線程工作時,寫入線程是不能訪問數據的