读写锁分为读锁和写锁,多个读锁之间是不需要互斥的(读操作不会改变数据,如果上了锁,反而会影响效率),写锁和写锁之间需要互斥,也就是说,如果只是读数据,就可以多个线程同时读,但是如果你要写数据,就必须互斥,使得同一时刻只有一个线程在操作。
案例:三个线程读数据,三个线程写数据。
class ReadWrite { /* 共享数据,只能一个线程写数据,可以多个线程读数据 */ private Object data = null; /* 创建一个读写锁 */ ReadWriteLock rwlock = new ReentrantReadWriteLock(); /** * 读数据,可以多个线程同时读, 所以上读锁即可 */ public void get() { /* 上读锁 */ rwlock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 准备读数据!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); System.out.println(Thread.currentThread().getName() + "读出的数据为 :" + data); } catch (InterruptedException e) { e.printStackTrace(); } finally { rwlock.readLock().unlock(); } } /** * 写数据,多个线程不能同时 写 所以必须上写锁 * * @param data */ public void put(Object data) { /* 上写锁 */ rwlock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 准备写数据!"); /* 休眠 */ Thread.sleep((long) (Math.random() * 1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " 写入的数据: " + data); } catch (Exception e) { e.printStackTrace(); } finally { rwlock.writeLock().unlock(); } } } /** * 测试类 * * @author Liao * */ public class ReadWriteLockTest { public static void main(String[] args) { /* 创建ReadWrite对象 */ final ReadWrite readWrite = new ReadWrite(); /* 创建并启动3个读线程 */ for (int i = 0; i < 3; i++) { new Thread(new Runnable() { @Override public void run() { readWrite.get(); } }).start(); /*创建3个写线程*/ new Thread(new Runnable() { @Override public void run() { /*随机写入一个数*/ readWrite.put(new Random().nextInt(8)); } }).start(); } } }
程序运行的结果:
从图中我们可以看出:上了写锁之后写数据必须同完成即从准备写数据到写入数据必须一气呵成!而上了读锁则不同,可以看到有多个线程同时准备读数据,又有多个线程同时读出数据。
下面我们通过这个读写锁的功能来模拟一个Hibernate的缓存:
public class HibernateCache { /* 定义一个Map来模拟缓存 */ private Map<String, Object> cache = new HashMap<String, Object>(); /* 创建一个读写锁 */ private ReadWriteLock rwLock = new ReentrantReadWriteLock(); /** * 模拟Hibernate缓存 * @param key * @return */ private Object getData(String key) { /* 上读锁 */ rwLock.readLock().lock(); /* 定义从缓存中读取的对象 */ Object value = null; try { /* 从缓存中读取数据 */ value = cache.get(key); if (value == null) { /* 如果缓存中没有数据,我们就把读锁关闭,直接上写锁【让一个线程去数据库中取数据】 */ rwLock.readLock().unlock(); /* 上写锁 */ rwLock.writeLock().lock(); try { /* 上了写锁之后再判断一次【我们只让一个线程去数据库中取值即可,当第二个线程过来的时候,发现value不为空了就去缓存中取值】 */ if (value == null) { /* 模拟去数据库中取值 */ value = "hello"; } } finally { /* 写完之后把写锁关闭 */ rwLock.writeLock().unlock(); } /* 缓存中已经有了数据,我们再把已经 关闭的读锁打开 */ rwLock.readLock().lock(); } } finally { /* 最后把读锁也关闭 */ rwLock.readLock().unlock(); } return value; } }