一、简介
ReadWriteLock是JDK5中提供的读写分离锁。读写分离锁可以有效地帮助减少锁竞争,以提升系统性能。用锁分离的机制来提升性能非常容易理解,比如线程A1、A2、A3进行写操作,B1、B2、B3进行读操作,如果使用重入锁或者内部锁,则理论上说所有读之间、读与写之间、写和写之间都是串行操作。当B1进行读取时,B2、B3则需要等待锁。由于读操作并不对数据的完整性造成破坏,这种等待显然是不合理。因此,读写锁就有了发挥功能的余地。
在这种情况下,读写锁允许多个线程同时读,使得B1、B2、B3之间真正并行。但是,考虑到数据完整性,写写操作和读写操作间依然是需要相互等待和持有锁的。总的来说,读写锁的访问约束如表3.1所示。
| 读 | 写 | |
| 读 | 非阻塞 | 阻塞 |
| 写 | 阻塞 | 阻塞 |
- 读-读不互斥:读读之间不阻塞。
- 读-写互斥:读阻塞写,写也会阻塞读。
- 写-写互斥:写写阻塞。
如果在系统中,读操作次数远远大于写操作,则读写锁就可以发挥最大的功效,提升系统的性能。
二、使用
ReentrantReadWriteLock定义了readLock()返回读锁,定义了writeLock()返回写锁.注意readLock()与writeLock()方法返回的锁对象是同一个锁的两个不同的角色,不是分别获得两个不同的锁。ReadWriteLock接口实例可以充当两个角色。读写锁的基本使用方法
//定义读写锁
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//获得读锁
Lock readLock = readWriteLock.readLock();
//获得写锁
Lock writeLock = readWriteLock.writeLock();
//读数据
readLock.lock(); //申请读锁
try {
读取共享数据
}finally {
readLock.unlock(); //总是在finally 子句中释放锁
}
//写数据
writeLock.lock(); //申请写锁
try {
更新共享数据
}finally {
writeLock.unlock(); //总是在finally 子句中释放锁
}
举例:
public class ReadWriteLockDemo { 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); // 读操作的耗时越多,读写锁的优势就越明显 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 readRunnale = new Runnable() { @Override public void run() { try { demo.handleRead(readLock); } catch (InterruptedException e) { e.printStackTrace(); } } }; Runnable writeRunnale = new Runnable() { @Override public void run() { try { demo.handleWrite(writeLock, new Random().nextInt()); } catch (InterruptedException e) { e.printStackTrace(); } } }; for (int i = 0; i < 18; i++) { new Thread(readRunnale).start(); } for (int i = 18; i < 20; i++) { new Thread(writeRunnale).start(); } } }