1.排他锁(互斥锁)的概念:java
synchronized,ReentrantLock这些锁都是排他锁,这些锁同一时刻只容许一个线程进行访问。缓存
2.读写锁的概念:dom
分为读锁和写锁,多个读锁不互斥,读锁和写锁互斥,写锁与写锁互斥。性能
3.读写锁的好处:this
为了提升性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,若是没有写锁的状况下,读是无阻塞的,在必定程度上提升了程序的执行效率。.net
原来使用的互斥锁只能同时间有一个线程在运行,如今的读写锁同一时刻能够多个读锁同时运行,这样的效率比原来的排他锁(互斥锁)效率高。线程
4.读写锁的原理分析:code
Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,接口
lock方法 是基于CAS 来实现的进程
public interface ReadWriteLock { /** * Returns the lock used for reading. * * @return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * @return the lock used for writing. */ Lock writeLock(); }
5.案例一:
package WriteReadLock; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class ReadWriterLockTest { public static void main(String[] args) { final Queue q3 = new Queue(); for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.get(); } } }.start(); } for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.put(new Random().nextInt(10000)); } } }.start(); } } } class Queue{ //共享数据,只能有一个线程能写该数据,但能够有多个线程同时读该数据。 private Object data = null; //获得读写锁 ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * 将用于读的get()和写的put()放在同一个类中这样是为了对同一个资源data进行操做,造成互斥 */ /** * 进行读操做 * 能够多个读线程同时进入,写线程不能执行 */ public void get(){ //获取读锁,并加锁 Lock readLock = readWriteLock.readLock(); readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "have read data :"+ data); } catch (InterruptedException e) { e.printStackTrace(); }finally { //!!!!!!注意:锁的释放必定要在trycatch的finally中,由于若是前面程序出现异常,锁就不能释放了 //释放读锁 readLock.unlock(); } } /** * 进行写操做 * 只能一个写线程进入,读线程不能执行 */ public void put(Object data){ //获取写锁,并加锁 Lock writeLock = readWriteLock.writeLock(); writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep((long)(Math.random()*1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放写锁 writeLock.unlock(); } } }
没有使用锁以前:读和写交叉在一块儿
在加入读写锁以后:读的过程当中,不会有写
6.案例二:
package WriteReadLock; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { //用map来模拟缓存 Map<String,Object> cache = new HashMap<String,Object>(); ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public static void main(String[] args) { final CacheDemo cacheDemo = new CacheDemo(); for(int i=0;i<6;i++) { new Thread(){ public void run(){ while(true){ System.out.println( cacheDemo.getData("key1").toString()); } } }.start(); } } Lock readLock = readWriteLock.readLock(); Lock writeLock = readWriteLock.writeLock(); //这里必需要用volatie当一个写线程设置value="aaaabbbb",必定要让其余的线程知道vlue的变化,这样就不会被重复写 volatile Object value; public Object getData(String key){ readLock.lock(); try { Thread.sleep(300); System.out.println(" read"); value = cache.get(key); if (value == null) { //这里已经加了读锁,读锁中写是不能容许的,因此要把这个锁释放掉 readLock.unlock(); writeLock.lock(); //防止,当多个写者进程在等待,前面的写进程已经赋值了,value已经不为空了后面的等着的写进程仍然继续赋值 if(value == null){ System.out.println("find null"); value="aaaabbbb"; cache.put(key, value); System.out.println("write"); } writeLock.unlock(); //重新加上读锁 readLock.lock(); } return value; } catch (Exception e) { e.printStackTrace(); }finally { readLock.unlock(); } return null; } }