简介

  • 重入锁 ReentrantLock 是排他锁,排他锁在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服务,而写服务占有的时间较少。然而,读服务不存在数据竞争问题,如果一个线程在读时禁止其他线程读势必会导致性能降低。所以就提供了读写锁。

  • 读写锁维护着一对锁,一个读锁和一个写锁。通过分离读锁和写锁,使得并发性比一般的排他锁有了较大的提升:

    • 在同一时间,可以允许多个读线程同时访问。
    • 是,在写线程访问时,所有读线程和写线程都会被阻塞。
  • 读写锁的主要特性:
    • 公平性:支持公平性和非公平性。
    • 重入性:支持重入。读写锁最多支持 65535 个递归写入锁和 65535 个递归读取锁。
    • 锁降级:遵循获取写锁,再获取读锁,最后释放写锁的次序,如此写锁能够降级成为读锁。

      示例

  • ReadWriteLock

    • ReadWriteLock也是一个接口,在它里面只定义了两个方法:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      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();
      }
    • 一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。下面的ReentrantReadWriteLock实现了ReadWriteLock接口。

  • 4.ReentrantReadWriteLock

    • ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁。
    • 下面通过几个例子来看一下ReentrantReadWriteLock具体用法。
    • 假如有多个线程要同时进行读操作的话,先看一下synchronized达到的效果:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      public class Test {
      private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

      public static void main(String[] args) {
      final Test test = new Test();

      new Thread(){
      public void run() {
      test.get(Thread.currentThread());
      };
      }.start();

      new Thread(){
      public void run() {
      test.get(Thread.currentThread());
      };
      }.start();

      }

      public synchronized void get(Thread thread) {
      long start = System.currentTimeMillis();
      while(System.currentTimeMillis() - start <= 1) {
      System.out.println(thread.getName()+"正在进行读操作");
      }
      System.out.println(thread.getName()+"读操作完毕");
      }
      }
    • 这段程序的输出结果会是,直到thread1执行完读操作之后,才会打印thread2执行读操作的信息。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0正在进行读操作
      Thread-0读操作完毕
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1正在进行读操作
      Thread-1读操作完毕
    • 而改成用读写锁的话:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      public class Test {
      private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

      public static void main(String[] args) {
      final Test test = new Test();

      new Thread(){
      public void run() {
      test.get(Thread.currentThread());
      };
      }.start();

      new Thread(){
      public void run() {
      test.get(Thread.currentThread());
      };
      }.start();

      }

      public void get(Thread thread) {
      rwl.readLock().lock();
      try {
      long start = System.currentTimeMillis();

      while(System.currentTimeMillis() - start <= 1) {
      System.out.println(thread.getName()+"正在进行读操作");
      }
      System.out.println(thread.getName()+"读操作完毕");
      } finally {
      rwl.readLock().unlock();
      }
      }
      }
      • 此时打印的结果为:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        Thread-0正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-0正在进行读操作
        Thread-0正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0正在进行读操作
        Thread-1正在进行读操作
        Thread-0读操作完毕
        Thread-1读操作完毕
      • 说明thread1和thread2在同时进行读操作。这样就大大提升了读操作的效率。不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。

四.总结

参考转载