类ReentranLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentranLock.lock方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率确是非常低下的。所以引入了一种读写锁ReentranReadWriteLock类,使用它可以加快运行效率。
读写锁表示也有两个锁,一个是读操作相关的锁,也称为共享锁;另一个是写操作相关的锁,也叫排它锁。也就是多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥。
一、读读共享
public class Test5 { public static void main(String[] args) { ServiceRR serviceRR = new ServiceRR(); Thread t1 = new Thread(){ @Override public void run() { serviceRR.read(); } }; Thread t2 = new Thread(){ @Override public void run() { serviceRR.read(); } }; Thread t3 = new Thread(){ @Override public void run() { serviceRR.read(); } }; t1.start(); t2.start(); t3.start(); } } class ServiceRR{ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void read(){ try { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+"获得读锁"); Thread.sleep(2000); }finally { lock.readLock().unlock(); } }catch (Exception e){ e.printStackTrace(); } } }上述代码有三个线程,都在同时进行读操作。
由结果来看,read方法中获得读锁后休眠1秒,此时未释放锁,而此时其他两个线程也进入了read方法并获得了读锁。可见读读是共享的。
二、写写互斥
public class Test6 { public static void main(String[] args) { ServiceWW serviceWW = new ServiceWW(); Thread t1 = new Thread(){ @Override public void run() { serviceWW.write(); } }; Thread t2 = new Thread(){ @Override public void run() { serviceWW.write(); } }; Thread t3 = new Thread(){ @Override public void run() { serviceWW.write(); } }; t1.start(); t2.start(); t3.start(); } } class ServiceWW{ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void write(){ try { try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"获得写锁"); Thread.sleep(2000); }finally { lock.writeLock().unlock(); } }catch (Exception e){ e.printStackTrace(); } } }
上述代码有三个线程进行写操作
Thread-1休眠2秒钟期间没有线程获得写锁
Thread-0休眠完后,Thread-1获得写锁,在Thread-1休眠期间,最后一个线程也没有获得写锁
Thead-0和Thread-1都执行完后,Thread-2才获得写锁。由此可见写写是互斥的。
三、读写互斥
public class Test7 { public static void main(String[] args) { ServiceRW serviceRW = new ServiceRW(); Thread t1 = new Thread(){ @Override public void run() { serviceRW.read(); } }; Thread t2 = new Thread(){ @Override public void run() { serviceRW.write(); } }; t1.start(); t2.start(); } } class ServiceRW{ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public void read(){ try { try { lock.readLock().lock(); System.out.println(Thread.currentThread().getName()+"获得读锁"); Thread.sleep(2000); }finally { lock.readLock().unlock(); } }catch (Exception e){ e.printStackTrace(); } } public void write(){ try { try { lock.writeLock().lock(); System.out.println(Thread.currentThread().getName()+"获得写锁"); Thread.sleep(2000); }finally { lock.writeLock().unlock(); } }catch (Exception e){ e.printStackTrace(); } } }上述代码是一个读操作线程和一个写操作线程
可见,Thread-0获得读锁然后休眠时,写操作线程无法获取到写锁,可见读写是互斥的。
四、写读互斥
将读写互斥的代码的线程启动顺序交换,便是写读互斥,运行结果如下
可见,Thread-1获得写锁然后休眠时,读操作线程无法获取到写锁,可见写读是互斥的。
五、总结
读写,写读,写写都是互斥的,而读读是异步的,非互斥的。