读写锁
排他锁与共享锁
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。
读-读能共存,读-写不能共存,写-写不能共存
读写锁的实现
1 public class Demo { 2 private Map<String,Object> map = new HashMap<>(); 3 private ReadWriteLock rw = new ReentrantReadWriteLock(); 4 5 private Lock r = rw.readLock(); 6 private Lock w = rw.writeLock(); 7 8 9 10 public Object get(String key){ 11 r.lock(); 12 System.out.println(Thread.currentThread().getId()+"读操作正在执行"); 13 try { 14 try { 15 Thread.sleep(1000); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 return map.get(key); 20 }finally { 21 r.unlock(); 22 System.out.println(Thread.currentThread().getName()+"读操作执行完毕"); 23 } 24 } 25 26 public void put(String key ,Object value){ 27 w.lock(); 28 System.out.println(Thread.currentThread().getName()+"写操作正在执行"); 29 try { 30 try { 31 Thread.sleep(1000); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 map.put(key,value); 36 }finally { 37 w.unlock(); 38 System.out.println(Thread.currentThread().getName()+"写操作执行完毕"); 39 } 40 } 41 42 43 }
Main
1 public class Main { 2 3 public static void main(String[] args) { 4 Demo d = new Demo(); 5 new Thread(new Runnable() { 6 @Override 7 public void run() { 8 d.put("a",10); 9 10 } 11 }).start(); 12 13 new Thread(new Runnable() { 14 @Override 15 public void run() { 16 d.put("",10); 17 18 } 19 }).start(); 20 new Thread(new Runnable() { 21 @Override 22 public void run() { 23 d.put("c",10); 24 25 } 26 }).start(); 27 28 } 29 30 }
执行如下
Thread-0写操作正在执行
Thread-0写操作执行完毕
Thread-1写操作正在执行
Thread-1写操作执行完毕
Thread-2写操作正在执行
Thread-2写操作执行完毕
读锁
1 public class Main2 { 2 3 public static void main(String[] args) { 4 Demo d = new Demo(); 5 d.put("key1","value1"); 6 7 new Thread(new Runnable() { 8 @Override 9 public void run() { 10 System.out.println(d.get("key1")); 11 } 12 }).start(); 13 14 new Thread(new Runnable() { 15 @Override 16 public void run() { 17 System.out.println(d.get("key1")); 18 } 19 }).start(); 20 new Thread(new Runnable() { 21 @Override 22 public void run() { 23 System.out.println(d.get("key1")); 24 } 25 }).start(); 26 27 } 28 }
执行如下,速度比写锁块。
Thread-1读操作执行完毕
Thread-0读操作执行完毕
value1
value1
Thread-2读操作执行完毕
value1
ReentranReadWriteLock
WriteLock implements Lock, java.io.Serializable
1 public void lock() { 2 sync.acquire(1); 3 }
调用同步器的acquire()方法,独占排他锁
同步器创建取决于是公平的和非公平的,依然有三个内部类,Sync,NonfairSync,FairSync,
Sync继承了AQS,NonfairSync,FairSync继承了Sync
读写锁需要保存的状态,(int值表示重入的次数)
写锁重入的次数
读锁的个数
每个读锁重入的次数
较关注的就是tryAcquire,tryRelease,tryAcquiredShared,tryReleaseShared
1.
1 protected final boolean tryAcquire(int acquires) { 2 /* 3 * Walkthrough: 4 * 1. If read count nonzero or write count nonzero 5 * and owner is a different thread, fail. 6 * 2. If count would saturate, fail. (This can only 7 * happen if count is already nonzero.) 8 * 3. Otherwise, this thread is eligible for lock if 9 * it is either a reentrant acquire or 10 * queue policy allows it. If so, update state 11 * and set owner. 12 */ 13 Thread current = Thread.currentThread(); 14 int c = getState(); 15 int w = exclusiveCount(c); 16 if (c != 0) { 17 // (Note: if c != 0 and w == 0 then shared count != 0) 18 if (w == 0 || current != getExclusiveOwnerThread()) 19 return false; 20 if (w + exclusiveCount(acquires) > MAX_COUNT) 21 throw new Error("Maximum lock count exceeded"); 22 // Reentrant acquire 23 setState(c + acquires); 24 return true; 25 } 26 if (writerShouldBlock() || 27 !compareAndSetState(c, c + acquires)) 28 return false; 29 setExclusiveOwnerThread(current); 30 return true; 31 }
先看锁不重入的情况
writerShouldBlock() 返回 false,执行 compareAndSetState(c, c + acquires))
状态设置为1,返回 fasle,
最后把线程设置为当前线程 , setExclusiveOwnerThread(current);
w == 0, 说明是读锁请求这个方法。直接返回flase.
w + exclusiveCount(acquires) > MAX_COUNT ,w,写锁重入的次数+1 > MAX_COUNT
(1 << SHARED_SHIFT) - 1 = 65535,抛出异常。
否则重入成功 ,setState(c + acquires); 状态+1,返回true
2.
1 protected final boolean tryRelease(int releases) { 2 if (!isHeldExclusively()) 3 throw new IllegalMonitorStateException(); 4 int nextc = getState() - releases; 5 boolean free = exclusiveCount(nextc) == 0; 6 if (free) 7 setExclusiveOwnerThread(null); 8 setState(nextc); 9 return free; 10 }
首先判断是否是独占锁,不是抛出异常
是的话 getState() - releases; 状态 -1
exclusiveCount(nextc) == 0; 独占的数量是否为0 ,有,返回false,
没有,返回true,exclusiveCount(nextc) == 0; 把当前线程置为空
serState(nextc);保存0或-1的值
3.
1 protected final int tryAcquireShared(int unused) { 2 /* 3 * Walkthrough: 4 * 1. If write lock held by another thread, fail. 5 * 2. Otherwise, this thread is eligible for 6 * lock wrt state, so ask if it should block 7 * because of queue policy. If not, try 8 * to grant by CASing state and updating count. 9 * Note that step does not check for reentrant 10 * acquires, which is postponed to full version 11 * to avoid having to check hold count in 12 * the more typical non-reentrant case. 13 * 3. If step 2 fails either because thread 14 * apparently not eligible or CAS fails or count 15 * saturated, chain to version with full retry loop. 16 */ 17 Thread current = Thread.currentThread(); 18 int c = getState(); 19 if (exclusiveCount(c) != 0 && 20 getExclusiveOwnerThread() != current) 21 return -1; 22 int r = sharedCount(c); 23 if (!readerShouldBlock() && 24 r < MAX_COUNT && 25 compareAndSetState(c, c + SHARED_UNIT)) { 26 if (r == 0) { 27 firstReader = current; 28 firstReaderHoldCount = 1; 29 } else if (firstReader == current) { 30 firstReaderHoldCount++; 31 } else { 32 HoldCounter rh = cachedHoldCounter; 33 if (rh == null || rh.tid != getThreadId(current)) 34 cachedHoldCounter = rh = readHolds.get(); 35 else if (rh.count == 0) 36 readHolds.set(rh); 37 rh.count++; 38 } 39 return 1; 40 } 41 return fullTryAcquireShared(current); 42 }
当读线程,能拿成功,写线程,拿不成功
19 if (exclusiveCount(c) != 0 && 20 getExclusiveOwnerThread() != current) 21 return -1;
独占锁不为0,有写线程,并不是当前线程,return -1 拿不到
如果不阻塞,小于MAX_COUNT 并且设置成功,即能够进来
r==0,第一次进来,firstReaderHoldCount = 1;
private transient int firstReaderHoldCount; --- int类型的值
如果不等于0,firstReaderHoldCount++
如果两者都不满足,说明并非重入,而是有另外的线程进来
static final class HoldCounter { int count = 0; // Use id, not reference, to avoid garbage retention final long tid = getThreadId(Thread.currentThread()); }
HoldCounter 记录重入的次数,保存在当前线程,保存在ThreadLocal,保证线程安全性
1 static final class ThreadLocalHoldCounter 2 extends ThreadLocal<HoldCounter> { 3 public HoldCounter initialValue() { 4 return new HoldCounter(); 5 } 6 }
如果 rh == null rh.count++
4
1 protected final boolean tryReleaseShared(int unused) { 2 Thread current = Thread.currentThread(); 3 if (firstReader == current) { 4 // assert firstReaderHoldCount > 0; 5 if (firstReaderHoldCount == 1) 6 firstReader = null; 7 else 8 firstReaderHoldCount--; 9 } else { 10 HoldCounter rh = cachedHoldCounter; 11 if (rh == null || rh.tid != getThreadId(current)) 12 rh = readHolds.get(); 13 int count = rh.count; 14 if (count <= 1) { 15 readHolds.remove(); 16 if (count <= 0) 17 throw unmatchedUnlockException(); 18 } 19 --rh.count; 20 } 21 for (;;) { 22 int c = getState(); 23 int nextc = c - SHARED_UNIT; 24 if (compareAndSetState(c, nextc)) 25 // Releasing the read lock has no effect on readers, 26 // but it may allow waiting writers to proceed if 27 // both read and write locks are now free. 28 return nextc == 0; 29 } 30 }
- 锁降级就是把写锁降级为读锁
- 在写锁没有释放的时候,获取到读锁,再释放写锁
1 private Map<String,Object> map = new HashMap<String,Object>(); 2 //读写锁 3 private ReadWriteLock lock =new ReentrantReadWriteLock(); 4 //读锁 5 private Lock readLock = lock.readLock(); 6 //写锁 7 private Lock writeLock =lock.writeLock(); 8 9 private volatile boolean flag; 10 11 public void readwrite() { 12 //为了保证flag能拿到最新的值 13 readLock.lock(); 14 if(flag) { 15 //对值进行写操作,因为读写锁互斥,若不释放读锁,则写锁无法获取 16 readLock.unlock(); 17 //获取写锁 读锁释放完毕后,所有写锁竞争线程 18 writeLock.lock(); 19 //写锁是排它锁,最终有一个线程获得写锁,并执行put写操作 20 map.put("hello", "hi"); 21 //在写完后,若不加读锁,则直接释放读锁,其他线程可能同样进行put()写操作 22 //在此加了读锁后,读写锁是互斥的,其他线程必须等待readLock读锁释放后才能写(put )成功 23 readLock.lock(); //获取读锁进行锁降级 24 //释放写锁 25 writeLock.unlock(); 26 } 27 Object value = map.get("hello"); 28 System.out.println(value); 29 readLock.unlock(); 30 }
锁降级的作用
1.保证同一线程内数据的可见性。 2.提高并发的性能
完