带你实现一个你上你也行的读写锁

是啥我就不多BB了,各位在学习JUC前先简单实现一个自己的读写锁岂不美哉。

带你实现一个你上你也行的读写锁

读写锁

* 读写锁
**/
public class ReadWriteLock {
  private int readingReaders = 0; //正在读的线程数
  private int waitingReaders = 0; //正在等待锁的读线程数
  private int writingWriters = 0; //正在写的线程数
  private int waitingWriters = 0; //正在等待锁的写线程数
  private boolean readerFirst = true; //写者优先
  /**
   * 无参构造,默认读者优先
   */
  public ReadWriteLock() {
      this(true);
  }

  public ReadWriteLock(boolean preferReader) {
      this.readerFirst = preferReader;
  }

  /**
   * 读锁
   */
  public synchronized void readLock() throws InterruptedException {
      //有读线程需要来获取锁,读等待线程数量加1.
      this.waitingReaders++;
      try {
          //如果有读线程正在写,则阻塞。不阻塞读,到达读读无锁的目的。
          while (writingWriters > 0) {
              this.wait();
          }
          //没有写线程正在写,则可以进行读了,并将正在读线程数量++。
          this.readingReaders++;
      }finally {
          //等待读线程数量--
          this.waitingReaders--;
      }
  }

  /**
   * 读解锁
   */
  public synchronized void readUnlock() {
      //读完成后,正在读线程数量--
      this.readingReaders--;
      //唤醒所有被阻塞的线程,能被读解锁唤醒的阻塞线程一定是写线程。
      this.notifyAll();
  }

  /**
   * 写锁
   */
  public synchronized void writeLock() throws InterruptedException {
      //有写线程需要来获取锁,写等待线程数量加1.
      this.waitingWriters++;
      try {
          //如果有正在写的线程 或 有正在读的线程 或 有等待的读线程(读者优先),则当前写线程阻塞。
          while (writingWriters > 0 || readingReaders > 0 || (readerFirst && waitingReaders > 0)) {
              this.wait();
          }
          //如果无,则可以开始进行写,正在写线程数量++
          this.writingWriters++;
      } finally {
          //等待读线程数量--
          this.waitingWriters--;
      }
  }

  /**
   * 写解锁
   */
  public synchronized void writeUnlock() {
      //写完成后,正在写线程数量--
      this.writingWriters--;
      //唤醒所有被阻塞的线程,读写皆有可能。
      this.notifyAll();
  }

  public int getWaitingWriters(){
      return waitingWriters;
  }

  public int getWaitingReaders(){
      return waitingReaders;
  }
}
复制代码

上文代码通过条件判断统计参数以及阻塞的方式,实现了一个简单的“读写”、“写写”互斥的锁,但是“读读”不互斥。

如果写线程获取锁之前有正在等待读(之前有人正在写,阻塞住了),那么写线程阻塞进行等待,优先让给正在等待读的线程。

注:如果想更改为写者优先的话,可以更改代码,增加标志位,或者是标志位复用的方式,并且更改循环里判断条件即可。

共享资源

/*
 * 共享资源
 **/
public  class SharedData {

    private  String value;

    private final  ReadWriteLock lock = new ReadWriteLock();

    public SharedData() {
        value = "";
    }

    public String read() throws InterruptedException {
        try {
            lock.readLock();
            System.out.println("正在写等待的线程数量 :"+lock.getWaitingWriters());
            System.out.println("正在读等待的线程数量 :"+lock.getWaitingReaders());
            System.out.println(Thread.currentThread().getName() + " 读到了: " + this.value);
            System.out.println("------------------------------------------------");
            sleep(100);
            return this.value;
        }  finally {
            lock.readUnlock();
        }
    }

    public void write(String s) throws InterruptedException {
        try {
            lock.writeLock();
            System.out.println("正在写等待的线程数量:"+lock.getWaitingReaders());
            System.out.println("正在读等待的线程数量 :"+lock.getWaitingReaders());
            System.out.println(Thread.currentThread().getName() + " 写了: " + s);
            System.out.println("------------------------------------------------");
            sleep(200);
            this.value = s;
        } finally {
            lock.writeUnlock();
        }
    }

    private void sleep(int ms) throws InterruptedException {
        Thread.sleep(ms);
    }
}
复制代码

模拟读和写的工作线程

读工作线程

* 模拟读线程
*/
public class ReaderWorker extends Thread {

   private static final Random random = new Random(System.currentTimeMillis());

   //共享资源
   private final SharedData data;

   public ReaderWorker(SharedData data) {
       this.data = data;
   }

   @Override
   public void run() {
       try {
           while (true) {
               String s = data.read();
               sleep(random.nextInt(1000));
           }
       } catch (InterruptedException e) {
           System.out.println(Thread.currentThread().getName()+"被中断了~");
           return;
       }
   }
} 
复制代码

写工作线程

* 模拟写线程
 */
public class WriterWorker extends Thread {

    private static final Random random = new Random(System.currentTimeMillis());

    private final SharedData data;

    private String s;

    public WriterWorker(SharedData data, String s) {
        this.data = data;
        this.s = s;
    }

    @Override
    public void run() {
        try {
            while (true) {
                data.write(s);
                Thread.sleep(random.nextInt(1000));
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码

国际惯例主线程psvm

* 读写锁test
 */
public class ReadWritLockClient {
    public static void main(String[] args) {
        final SharedData sharedData = new SharedData();
        new ReaderWorker(sharedData).start();
        new ReaderWorker(sharedData).start();
        new WriterWorker(sharedData, "ABC").start();
        new WriterWorker(sharedData, "DEF").start();
        new WriterWorker(sharedData, "GHI").start();
        new WriterWorker(sharedData, "JKL").start();
        new WriterWorker(sharedData, "LMN").start();
    }
} 
复制代码

模拟若干个读写线程进行共享资源竞争

验证

带你实现一个你上你也行的读写锁

不会出现“读写”、“写写”的情况~ 完事

带你实现一个你上你也行的读写锁

本文使用 mdnice 排版

原文 

https://juejin.im/post/5e83058651882573be11b4e9

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 带你实现一个你上你也行的读写锁

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址