ReadWriteLock:见词知意,读写锁。正常业务中读操作往往比写操作更多,然后用独占锁去加锁,就会造成同样的读操作,只能排队等待,业务执行效率会降低,JDK1.5之后添加了读写锁ReadWriteLock,是一个接口,提供了readLock和writeLock两种锁的操作机制,一个读锁(共享锁),一个是写锁(排它锁)。特性为:读读共享,读写互斥,写写互斥。
ReadWriteLock底层也是基于AQS实现的,独占锁往往用int类型的全局变量state(0或者>0)来判断当前锁是否被占用,而共享锁是有读锁和写锁两种,并且锁的资源还是一个,如何区分?实现方式就是将一位int类型的4个字节(32位)分为高16位和低16位,高16位用来表示读锁状态,低16位用来表示写锁状态。读锁的加锁释放锁对高16位进行操作(+|-),写锁的加锁释放锁对低16位进行操作(+|-)。这样就能判读当前读写锁的状态和数量,由于只占16位,所以读写锁数量限制都为2^16-1。 获取写锁数量:当前状态值&0x0000FFFF,结果为0,无写锁,>0为写锁重入次数。写锁+1就是当前状态值+1,写锁-1就是当前状态值-1。 获取读锁数量:当前状态值>>>16,右移16位,得到的结果就是读锁的数量。 读锁+1就是,当前状态值 +(1<<16),-1就是S - (1<<16),超出会抛出异常。 写锁获取锁: 1.首先通过当前状态值,判断锁是否被占用,如果状态值为0,锁资源当前未被占用,cas自旋修改状态值,修改成功获取锁。 2.当前状态值为不为0,说明锁资源被占用,通过当前状态值&0x0000FFFF结果w判断当前写锁数量,如果w为0,则说明锁资源被读锁占用,竞争锁失败。如果w不为0,则判断持有锁的线程是否为当前线程,如果不是,竞争锁失败。如果是则竞争锁成功,执行逻辑,因为读写锁是可重入的。 读锁获取锁: 1.首先通过当前状态值,判断锁资源是否被占用,如果状态值为0,锁资源当前未被占用,cas自旋修改状态值,修改成功获取锁。 2.当前状态值为不为0,说明锁资源被占用,通过当前状态值&0x0000FFFF结果w判断当前写锁数量: 如果w>0:判断当前持有锁线程,是否为当前线程,如果是cas竞争锁,如果不是竞争锁失败。 如果w=0:判断是否超出最大值,是否需要等待,是否修改成功状态值,全成功获取所成功。
1.读读共享:
public static void main(String[] args) { ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //读锁1 Lock readLock1 = readWriteLock.readLock(); //读锁2 Lock readLock2 = readWriteLock.readLock(); new Thread(() -> { readLock1.lock(); System.out.println("模拟读1操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟读1操作结束"); readLock1.unlock(); }).start(); new Thread(() -> { readLock2.lock(); System.out.println("模拟读2操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟读2操作结束"); readLock2.unlock(); }).start(); }2.读写互斥:
public static void main(String[] args) { ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //读锁 Lock read = readWriteLock.readLock(); //写锁 Lock write = readWriteLock.writeLock(); new Thread(() -> { read.lock(); System.out.println("模拟读操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟读操作结束"); read.unlock(); }).start(); new Thread(() -> { write.lock(); System.out.println("模拟写操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟写操作结束"); write.unlock(); }).start(); }3.写写互斥:
public static void main(String[] args) { ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //读锁 Lock write1 = readWriteLock.readLock(); //写锁 Lock write2 = readWriteLock.writeLock(); new Thread(() -> { write1.lock(); System.out.println("模拟写1操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟写1操作结束"); write1.unlock(); }).start(); new Thread(() -> { write2.lock(); System.out.println("模拟写2操作开始"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("模拟写操作结束"); write2.unlock(); }).start(); }注意:读写锁支持锁降级,不支持锁升级: 锁降级:线程获取到了写锁,未释放写锁的情况下,获取读锁,是可以的。线程先拥有写锁,其他线程获取不到锁,无风险。 锁升级:线程获取到了读锁,在没有释放读锁的前提下,又获取写锁。如t1,t2都获取到读锁,在t1想要获取写锁时,t2有读锁,获取失败,t2想获取写锁时,t1有读锁,获取失败,造成死锁。
1.ReadWriteLock是接口,synchronized是关键字。 2.ReadWriteLock是共享锁,synchronized是排它锁。 3.ReadWriteLock读操作多时并发业务效率高。 4.都支持可重入,ReadWriteLock可设置为公平锁,synchronized为非公平锁。 5.ReadWriteLock是手动释放锁,synchronized是jvm管理。