读写锁:多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。即:读的时候不允许写,写的时候不允许读,可以同时读。
synchronized关键字和普通的Lock构造的锁,会造成读与读之间的互斥,因此读写锁可提高性能。
例子1:三个线程同时对一个共享数据进行读写。
1 import java.util.Random; 2 import java.util.concurrent.locks.ReadWriteLock; 3 import java.util.concurrent.locks.ReentrantReadWriteLock; 4 5 public class ReadWriteLockTest { 6 public static void main(String[] args) { 7 final Queue queue = new Queue(); 8 for (int i = 0; i < 3; i++) { 9 new Thread() { 10 public void run() { 11 while (true) { 12 queue.get(); 13 } 14 } 15 16 }.start(); 17 18 new Thread() { 19 public void run() { 20 while (true) { 21 queue.put( new Random().nextInt(10000)); 22 } 23 } 24 25 }.start(); 26 } 27 28 } 29 } 30 31 class Queue { 32 private Object data = null; // 共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 33 ReadWriteLock rwl = new ReentrantReadWriteLock(); 34 35 public void get() { 36 rwl.readLock().lock(); 37 try { 38 System. out.println(Thread.currentThread().getName() + " be ready to read data!"); 39 Thread. sleep((long) (Math. random() * 1000)); 40 System. out.println(Thread.currentThread().getName() + " have read data :" + data); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } finally { 44 rwl.readLock().unlock(); 45 } 46 } 47 48 public void put(Object data) { 49 50 rwl.writeLock().lock(); 51 try { 52 System. out.println(Thread.currentThread().getName() + " be ready to write data!"); 53 Thread. sleep((long) (Math. random() * 1000)); 54 this.data = data; 55 System. out.println(Thread.currentThread().getName() + " have write data: " + data); 56 } catch (InterruptedException e) { 57 e.printStackTrace(); 58 } finally { 59 rwl.writeLock().unlock(); 60 } 61 62 } 63 }
例子2:缓存实例
1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.concurrent.locks.ReadWriteLock; 4 import java.util.concurrent.locks.ReentrantReadWriteLock; 5 6 public class CacheDemo { 7 8 private static Map<String, Object> cache = new HashMap<String, Object>(); 9 10 private ReadWriteLock rwl = new ReentrantReadWriteLock(); 11 12 public Object getData(String key) { 13 // 当线程开始读时,首先开始加上读锁 14 rwl.readLock().lock(); 15 Object value = null; 16 try { 17 value = cache.get(key); 18 // 判断是否存在值 19 if (value == null) { 20 // 在开始写之前,首先要释放读锁,否则写锁无法拿到 21 rwl.readLock().unlock(); 22 // 获取写锁开始写数据 23 rwl.writeLock().lock(); 24 try { 25 // 再次判断该值是否为空,因为如果两个写线程都阻塞在这里, 26 // 当一个线程被唤醒后value的值为null则进行数据加载,当另外一个线程也被唤醒如果不判断就会执行两次写 27 if (value == null) { 28 value = "" ; // query 数据库 29 cache.put(key, value); 30 } 31 } finally { 32 rwl.writeLock().unlock(); // 释放写锁 33 } 34 rwl.readLock().lock(); // 写完之后降级为读锁 35 } 36 } finally { 37 rwl.readLock().unlock(); // 释放读锁 38 } 39 40 return value; 41 } 42 43 }