zoukankan      html  css  js  c++  java
  • 读写锁

    排他锁与共享锁

    读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。

    读-读能共存,读-写不能共存,写-写不能共存

    读写锁的实现

     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.提高并发的性能

  • 相关阅读:
    Linux下命令设置别名--alias(同实用于mac)
    mac 下配置连接Linux服务器方法,上传下载文件操作
    Jdbc和工具类
    MySQL和数据库
    validate和bootstrap学习
    jQuery学习
    JavaScripe学习
    CSS学习
    HTML学习
    Metail Design入门(一)
  • 原文地址:https://www.cnblogs.com/quyangyang/p/11172009.html
Copyright © 2011-2022 走看看