读写锁(ReentrantReadWriteLock),通过名字就应该知道肯定和Lock有关系。Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock。
在多线程中,为了提高效率有些共享资源允许同时进行多个读的操作,但只允许一个写的操作,比如一个文件,只要其内容不变可以让多个线程同时读,不必做排他的锁定,排他的锁定只有在写的时候需要,以保证别的线程不会看到数据不完整的文件。这时候就需要使用读写锁。
下面这个例子模拟了缓存系统:
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
public static void main(String[] args) {
}
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object getData(String key){
rwl.readLock().lock(); //添加读锁
Object value = null;
try{
value = cache.get(key);
if(value == null){
rwl.readLock().unlock(); //释放读锁
rwl.writeLock().lock(); //添加写锁
try{
if(value==null){
value = "aaaa";//去查数据库queryDB();
}
}finally{
rwl.writeLock().unlock();
}
rwl.readLock().lock();
}
}finally{
rwl.readLock().unlock();
}
return value;
}
}
通过一个例子了解读写锁:
public class ReadWriteLockTest {
public static void main(String[] args) {
final TheData myData = new TheData(); // 这是各线程的共享数据
for (int i = 0; i < 3; i++) { // 开启3个读线程
new Thread(new Runnable() {
public void run() {
while (true) {
myData.get();
}
}
}).start();
}
for (int i = 0; i < 3; i++) { // 开启3个写线程
new Thread(new Runnable() {
public void run() {
while (true) {
myData.put(new Random().nextInt(10000));
}
}
}).start();
}
}
}
class TheData {
private Object data = 200;
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get() {
rwl.readLock().lock(); // 读锁开启,读线程均可进入
try { // 用try finally来防止因异常而造成的死锁
System.out.println(Thread.currentThread().getName()
+ "is ready to read");
Thread.sleep(new Random().nextInt(5000));
System.out.println(Thread.currentThread().getName()
+ "have read date" + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwl.readLock().unlock(); // 读锁解锁
}
}
public void put(Object data) {
rwl.writeLock().lock(); // 写锁开启,这时只有一个写线程进入
try {
System.out.println(Thread.currentThread().getName()
+ "is ready to write");
Thread.sleep(new Random().nextInt(5000));
this.data = data;
System.out.println(Thread.currentThread().getName()
+ "have write date" + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rwl.writeLock().unlock(); // 写锁解锁
}
}
}
读锁可以允许多个进行读操作的线程同时进入,但不允许写进程进入;
写锁只允许一个写进程进入,在这期间任何进程都不能再进入。
要注意的是每个读写锁都有挂锁和解锁,最好将每一对挂锁和解锁操作都用try、finally来套入中间的代码,这样就会防止因异常的发生而造成死锁得情况。