1、引入ReadWriteLock讀寫鎖
ReadWriteLock是JDK5中提供的讀寫分離鎖。讀寫分離鎖可以有效地幫助減少鎖競(jìng)爭,以提升系統(tǒng)的性能。用鎖分離的機(jī)制來提升性能非常容易理解,比如線程A1、A2、A3進(jìn)行寫操作,B1、B2、B3進(jìn)行讀操作,如果使用重入鎖或者內(nèi)部鎖,則理論上說所有讀之間,讀與寫之間、寫與寫之間都是串行操作。當(dāng)B1進(jìn)行讀取時(shí),B2、B3則需要等待鎖。由于讀操作并不會(huì)對(duì)數(shù)據(jù)的完整性造成破壞,這種等待顯然是不合理的。因此,讀寫鎖就有了發(fā)揮功能的余地。
在這種情況下,讀寫鎖允許多個(gè)線程同時(shí)讀,使得B1、B2、B3之間真正的并行。但是考慮到數(shù)據(jù)的完整性,寫寫操作和讀寫操作之間依然是需要相互等待和持有鎖的。總的來說讀寫鎖的訪問約束如下:
- 讀-讀不互斥:讀讀之間不阻塞
- 讀-寫互斥:讀阻塞寫,寫也會(huì)阻塞讀。
- 寫寫互斥:寫寫阻塞
如果在系統(tǒng)中,讀操作次數(shù)遠(yuǎn)遠(yuǎn)大于寫操作,則讀寫鎖就可以發(fā)揮最大的功效,提升系統(tǒng)的性能。
2、簡單演示一下ReadWriteLock對(duì)性能的幫助
演示代碼如下:
public class ReadWriteLockDemo
{
private static Lock lock = new ReentrantLock();
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
private int value;
public Object handleRead(Lock lock) throws InterruptedException
{
try
{
lock.lock(); //模擬讀操作
Thread.sleep(1000); //讀操作的耗時(shí)越多,讀寫鎖的優(yōu)勢(shì)就越明顯
return value;
}
finally
{
lock.unlock();
}
}
public void handleWrite(Lock lock, int index) throws InterruptedException
{
try
{
lock.lock(); //模擬寫操作
Thread.sleep(1000);
value = index;
}
finally
{
lock.unlock();
}
}
public static void main(String[] args)
{
final ReadWriteLockDemo demo = new ReadWriteLockDemo();
Runnable readRunnable = new Runnable()
{
@Override
public void run()
{
try
{
demo.handleRead(readLock);
// demo.handleRead(lock);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
};
Runnable writeRunnable = new Runnable()
{
@Override
public void run()
{
try
{
demo.handleWrite(writeLock, new Random().nextInt());
demo.handleWrite(lock, new Random().nextInt());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
};
for (int i = 0; i < 18; ++i)
{
new Thread(readRunnable).start();
}
for (int i = 18; i < 20; ++i)
{
new Thread(writeRunnable).start();
}
}
}
上述代碼中,在讀寫操作時(shí)分別模擬了一個(gè)非常耗時(shí)的操作,讓線程等到1秒鐘。他們分別對(duì)應(yīng)讀寫耗時(shí)。當(dāng)進(jìn)行讀操作時(shí),使用讀鎖,進(jìn)行寫操作時(shí),使用寫鎖。然后開啟18個(gè)讀線程和兩個(gè)寫線程。由于這里使用了讀寫分離,因此讀線程完全并行,而寫線程會(huì)阻塞讀,因此,實(shí)際上這段代碼運(yùn)行大約2秒就會(huì)結(jié)束(寫線程之間實(shí)際是串行的)。如果用重入鎖代替讀寫鎖,那么所有的讀寫線程之間都必須相互等待,因此整個(gè)程序的執(zhí)行時(shí)間將長達(dá)20余秒。