• JAVA多线程2_Lock


    一、ReentrantLock类

    1.使用ReentrantLock

    1. 创建reentrantlock.service包,在包下创建MyService类

      package reentrantlock.service;
      
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class MyService {
          private Lock lock =new ReentrantLock();
          public Condition condition = lock.newCondition();
          public void await()
          {
              try {
                  lock.lock();
                  System.out.println("await时间为"+System.currentTimeMillis());
                  condition.await();
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.unlock();
              }
          }
          public void signal()
          {
              try{
                  lock.lock();
                  System.out.println("signal时间为"+System.currentTimeMillis());
                  condition.signal();
              }finally {
                  lock.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

      即使用ReentrantLock的同步锁,并且使用它的Condition对象的一个await/signal方法来实现控制并处理线程的状态,使其进入等待或就绪状态。
      分别用MyService对象的await、signal方法进行实现。

    2. 创建ReentrantLock.thread包,并在包下创建ThreadA类

      package reentrantlock.thread;
      
      import reentrantlock.service.MyService;
      
      public class ThreadA extends Thread{
          private MyService myService;
          public ThreadA(MyService myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.await();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      即ThreadA线程类通过构造方法给MyService对象赋值,并调用该对象的await方法。

    3. 创建reentrantlock.thread包,并在包下创建ThreadB类

      package reentrantlock.thread;
      
      import reentrantlock.service.MyService;
      
      public class ThreadB extends Thread{
          private MyService myService;
          public ThreadB(MyService myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.signal();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16

      即ThreadB线程类通过构造方法给MyService对象赋值,并调用该对象的signal方法。

    4. 创建reentrantlock.test包,并在包下创建Run类

      package reentrantlock.test;
      
      import reentrantlock.service.MyService;
      import reentrantlock.thread.ThreadA;
      import reentrantlock.thread.ThreadB;
      
      public class Run {
          public static void main(String[] args) throws InterruptedException {
              MyService myService =new MyService();
              ThreadA threadA = new ThreadA(myService);
              threadA.start();
              Thread.sleep(3000);
              ThreadB threadB = new ThreadB(myService);
              threadB.start();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      即通过ThreadA 调用MyService 的await方法,主线程休眠3秒后,ThreadB 调用MyService 的signal方法。

    5. await()方法通过调用Unsafe.park方法使线程暂停运行。
      如果传入true,则第二个参数单位时间为毫秒,此时第二个单位时间为绝对时间

      //获取Unsafe类的theUnsafe属性
      Field f = Unsafe.class.getDeclaredField("theUnsafe");
      //给theUnsafe设置为可使用
      f.setAccessible(true);
      Unsafe unsafe = (Unsafe) f.get(null);
      System.out.println("begin"+System.currentTimeMillis());
      unsafe.park(true,System.currentTimeMillis()+3000);
      System.out.println("end"+System.currentTimeMillis());
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      如果传入false,则第二个参数单位时间为纳秒,此时为相对时间

      //获取Unsafe类的theUnsafe属性
      Field f = Unsafe.class.getDeclaredField("theUnsafe");
      //给theUnsafe设置为可使用
      f.setAccessible(true);
      Unsafe unsafe = (Unsafe) f.get(null);
      System.out.println("begin"+System.currentTimeMillis());
      unsafe.park(false,3000000000L);
      System.out.println("end"+System.currentTimeMillis());
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    6. signal()方法通过调用Unsafe.unpark方法唤醒线程。

      package reentrantlock.thread;
      
      import sun.misc.Unsafe;
      
      public class MyThread extends Thread{
          private Unsafe unsafe;
          private Thread mainThread;
          public MyThread(Unsafe unsafe, Thread mainThread) {
              this.unsafe = unsafe;
              this.mainThread = mainThread;
          }
      
          @Override
          public void run() {
              try {
                  Thread.sleep(6000);
                  unsafe.unpark(mainThread);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      package reentrantlock.test;
      
      import reentrantlock.thread.MyThread;
      import sun.misc.Unsafe;
      
      import java.lang.reflect.Field;
      
      public class Test2 {
          public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InterruptedException {
              //获取Unsafe类的theUnsafe属性
              Field f = Unsafe.class.getDeclaredField("theUnsafe");
              //给theUnsafe设置为可使用
              f.setAccessible(true);
              Unsafe unsafe = (Unsafe) f.get(null);
              MyThread myThread = new MyThread(unsafe, Thread.currentThread());
              //MyThread线程启动休眠6s,再调用unpark方法
              myThread.start();
              //main线程休眠0.2秒
              Thread.sleep(200);
              //开始打印时间为myThread休眠的200毫秒后,所以从begin到end的时间未5.8秒。
              System.out.println("begin"+System.currentTimeMillis());
              unsafe.park(false,0L);
              System.out.println("end"+System.currentTimeMillis());
          }
      }
      
      
      • 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

    2.使用ReentrantLock实现多对多

    1. 创建reentrantlock.service包,然后创建MyService3类

      package reentrantlock.service;
      
      import java.util.concurrent.locks.Condition;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class MyService3 {
          private Lock lock =new ReentrantLock();
          public Condition condition = lock.newCondition();
          private boolean hasValue = false;
          public void set()
          {
              try {
                  lock.lock();
                  while(hasValue==true)
                  {
                      condition.await();
                  }
                  System.out.println("生产");
                  hasValue=true;
                  condition.signalAll();
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.unlock();
              }
      
          }
          public void get()
          {
              try{
                  lock.lock();
                  while(hasValue==false)
                  {
                      condition.await();
                  }
                  System.out.println("消费");
                  hasValue=false;
                  condition.signalAll();
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.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

      即跟之前的wait/notifyAll机制的多对多一样,而ReentrantLock的多对多实现也是使用While来判断条件,使用Condition对象的signalAll方法来唤醒所有等待线程

    2. 创建reentrantlock.thread包,然后创建ThreadA3类

      package reentrantlock.thread;
      
      import reentrantlock.service.MyService3;
      
      public class ThreadA3 extends Thread{
          private MyService3 myService;
          public ThreadA3(MyService3 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.set();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    3. 创建reentrantlock.thread包,然后创建ThreadB3类

      package reentrantlock.thread;
      
      import reentrantlock.service.MyService3;
      
      public class ThreadB3 extends Thread{
          private MyService3 myService;
          public ThreadB3(MyService3 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.get();
          }
      }
      
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    4. 创建reentrantlock.test包,然后创建Run3类

      package reentrantlock.test;
      
      import reentrantlock.service.MyService3;
      import reentrantlock.thread.ThreadA3;
      import reentrantlock.thread.ThreadB3;
      
      public class Run3 {
          public static void main(String[] args) {
              MyService3 service = new MyService3();
              ThreadA3[] A = new ThreadA3[10];
              ThreadB3[] B = new ThreadB3[10];
              for (int i = 0; i < 10; i++) {
                  A[i] = new ThreadA3(service);
                  B[i] = new ThreadB3(service);
                  A[i].start();
                  B[i].start();
              }
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

      即分别创建10个ThreadA3和ThreadB3实例分别调用MyService3实例中的set和get方法。set则是生产操作,get则是消费操作。从而实现多对多。

    3.公平锁与非公平锁

    1. 创建reentrantlock.service包,在包下创建MyService4 类

      package reentrantlock.service;
      
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class MyService4 {
          public Lock lock;
          public MyService4(boolean fair)
          {
              lock = new ReentrantLock(fair);
          }
          public void testMethod()
          {
              try {
                  lock.lock();
                  System.out.println("testMethod"+Thread.currentThread().getName());
                  //此处的10ms用于配合 array2有更多可能在非公平的情况下找到锁
                  //如果线程数量够多,也可以不休眠或让休眠时间更小
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally{
                  lock.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

      即通过MyService4的构造方法给ReentrantLock传参,生成非公平锁对象。
      休眠是为了让其他线程有机会抢到锁对象。

    2. 创建reentrantlock.thread包,在包下创建MyThread2 类

      package reentrantlock.thread;
      
      import reentrantlock.service.MyService4;
      
      public class MyThread2 extends Thread{
          private MyService4 service4;
          public MyThread2(MyService4 service4)
          {
              super();
              this.service4 = service4;
          }
      
          @Override
          public void run() {
              service4.testMethod();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      即线程类用来调用MyService4的testMethod方法。

    3. 创建reentrantlock.test包,在包下创建Run4类

      package reentrantlock.test;
      
      import reentrantlock.service.MyService4;
      import reentrantlock.thread.MyThread2;
      
      public class Run4 {
          public static void main(String[] args) throws InterruptedException {
              //即通过MyService4的构造方法将bool值传入,再给ReentrantLock传入,生成非公平锁NonfairSync。
              MyService4 myService4 = new MyService4(false);
              MyThread2[] array1 = new MyThread2[1000];
              MyThread2[] array2 = new MyThread2[1000];
              long begin = System.currentTimeMillis();
              for (int i = 0; i < array1.length; i++) {
                  array1[i]= new MyThread2(myService4);
                  array1[i].setName("array1+++"+(i+1));
                  array1[i].start();
              }
              Thread.sleep(10);
              for (int i = 0; i < array2.length; i++) {
                  array2[i]= new MyThread2(myService4);
                  array2[i].setName("array2---"+(i+1));
                  array2[i].start();
              }
              
          }
      }
      
      
      • 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

    即通过MyService4的构造方法生成一个非公平锁,生成两个线程数组,使用for循环去启动线程,线程组1和线程组2中间的休眠是为了让线程组1先排队,而线程执行期间休眠是为了让线程组2可以在线程运行时,线程组1刚刚释放锁,线程组二有机会可以抢到线程组1刚刚释放的锁,如果线程量够大,线程执行时的休眠可以不要。
    非公平锁运行结果:
    在这里插入图片描述
    而公平锁就是将MyService4构造方法传入的值修改为true,它是按申请锁资源的顺序来给锁资源的。
    即上述结果中2不会在1之前。

    4.ReentrantLock类其他方法的使用

    1. public int getHoldCount()方法,作用是查询"当前线程"保持此锁的个数,也就是调用lock方法的次数。
      执行lock方法进行锁重入,导致count+1,而unlock方法导致count-1。

    2. public final int getQueueLength方法,作用是返回正等待获取此锁的线程的估计数。

    3. public int getWaitQueueLength(Condition condition)方法,作用是返回等待此锁相关的给定条件Condition的线程估计数。即返回调用过condition.await方法的线程估计数。

    4. public final boolean hasQueuedThread(Thread thread),作用是查询指定的线程是否正在等待获取此锁,即判断参数中的线程是否在等待队列中。

    5. public final boolean hasQueuedThreads(),作用是查询是否有线程正在等待获取此锁,即等待队列中是否有等待的线程。

    6. public boolean hasWaiters(Condition condition),作用是查询是否有线程正在等待与此锁有关的condition对象,即是否有线程执行了condition对象的await方法而呈等待状态。

    7. public final boolean isFair(),作用是判断是不是公平锁。
      ReentrantLock默认是非公平锁。

    8. public boolean isHeldByCurrentThread(),作用是查询当前线程是否持有此锁。

    9. public boolean isLocked(),作用是查询此锁是否有线程持有,并且未释放。

    10. public void lockInterruptibly(),作用是当某个线程尝试获得锁并且阻塞在lockInterruptibly()方法时,该线程可以被中断。
      即使用lockInterruptibly方法进行加锁时,该锁可以被中断(interrupt)。

    11. public boolean tryLock(),作用是当前线程发现锁被其他线程持有了,则返回false,那么程序继续执行后面的代码,而不是呈阻塞等待锁状态的。
      即可以将tryLock作为一个判断条件,true(获得锁)则执行true指定的语句,false执行false指定的语句。

    12. public boolean tryLock(long timeout,TimeUnit unit),如果在指定的timeout时间内持有了锁,则返回true,如果超过时间则返回false。

    13. public boolean await(long time,TimeUnit unit)和public final native void wait(long timeout)方法一样,具有自动唤醒功能。

    14. public long awaitNanos(long nanosTimeout),具有自动唤醒功能,时间单位是纳秒。
      1000纳秒=1微秒,1000微秒=1毫秒,1000毫秒=1秒。

    15. public boolean awaitUntil(Date deadline),作用是在指定的日期结束等待。
      13-15的方法都是具有自动唤醒功能的等待方法。

    16. public void awaitUninterruptibly(),作用是等待的过程中,不允许被中断。即将await方法换成awaitUninterruptibly方法。

    二、ReentrantReadWriteLock

    使用ReentrantLock对象时,对所有的操作都同步,包括读取操作,这样会耗费大量时间,降低运行效率。

    1.读读共享

    1. 创建一个reentrantreadwritelock.service包,在包下创建MyService 类

      package reentrantreadwritelock.service;
      
      import java.util.concurrent.locks.ReentrantReadWriteLock;
      
      public class MyService {
          private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
          private String username = "abc";
          public void testMethod()
          {
              try {
                  lock.readLock().lock();
                  System.out.println("begin "+Thread.currentThread().getName()+ System.currentTimeMillis());
                  System.out.println("print service"+ username);
                  Thread.sleep(4000);
                  System.out.println("end "+Thread.currentThread().getName()+""+System.currentTimeMillis());
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.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
    2. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadA类

      package reentrantreadwritelock.thread;
      
      import reentrantreadwritelock.service.MyService;
      
      public class ThreadA extends Thread{
          private MyService myService = new MyService();
          public ThreadA(MyService myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.testMethod();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    3. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadB类

      package reentrantreadwritelock.thread;
      
      import reentrantreadwritelock.service.MyService;
      
      public class ThreadB extends Thread{
          private MyService myService = new MyService();
          public ThreadB(MyService myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.testMethod();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    4. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadB类

      package reentrantreadwritelock.test;
      
      import reentrantreadwritelock.service.MyService;
      import reentrantreadwritelock.thread.ThreadA;
      import reentrantreadwritelock.thread.ThreadB;
      
      public class Run {
          public static void main(String[] args) {
              MyService myService =new MyService();
              ThreadA a = new ThreadA(myService);
              a.setName("a");
              a.start();
              ThreadB b = new ThreadB(myService);
              b.setName("b");
              b.start();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      即创建一个MyService 实例,将其传入ThreadA和ThreadB实例中,两个线程共用一把锁。读锁是共享锁,所以两个读操作不会互斥,可以同时进行读操作,减少同步耗费的时间。

    2.写写互斥

    1. 创建一个reentrantreadwritelock.service包,在包下创建MyService2 类

      package reentrantreadwritelock.service;
      
      import java.util.concurrent.locks.ReentrantReadWriteLock;
      
      public class MyService2 {
          private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
          private String username = "abc";
          public void write()
          {
              try {
                  lock.writeLock().lock();
                  System.out.println("获得写锁 "+Thread.currentThread().getName()+ System.currentTimeMillis());
                  Thread.sleep(10000);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.writeLock().unlock();
              }
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    2. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadA2 类

      package reentrantreadwritelock.thread;
      
      
      import reentrantreadwritelock.service.MyService2;
      
      public class ThreadA2 extends Thread{
          private MyService2 myService = new MyService2();
          public ThreadA2(MyService2 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.write();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    3. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadB2 类

      package reentrantreadwritelock.thread;
      
      import reentrantreadwritelock.service.MyService2;
      
      public class ThreadB2 extends Thread{
          private MyService2 myService = new MyService2();
          public ThreadB2(MyService2 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.write();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    4. 创建一个reentrantreadwritelock.test包,在包下创建Run2 类

      package reentrantreadwritelock.test;
      
      import reentrantreadwritelock.service.MyService2;
      import reentrantreadwritelock.thread.ThreadA2;
      import reentrantreadwritelock.thread.ThreadB2;
      
      public class Run2 {
          public static void main(String[] args) {
              MyService2 myService =new MyService2();
              ThreadA2 a = new ThreadA2(myService);
              a.setName("a");
              a.start();
              ThreadB2 b = new ThreadB2(myService);
              b.setName("b");
              b.start();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      即创建一个MyService2实例,2个线程传入同一个MyService2实例,线程调用MyService2同一个方法,该方法使用写锁,此时写写互斥,需要等第一个线程执行方法结束后,第二个线程才能执行方法内的任务。

    3.读写互斥

    1. 创建一个reentrantreadwritelock.service包,在包下创建MyService3 类

      package reentrantreadwritelock.service;
      
      import java.util.concurrent.locks.ReentrantReadWriteLock;
      
      public class MyService3 {
          private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
          public void read()
          {
              try {
                  lock.readLock().lock();
                  System.out.println("获得读锁"+Thread.currentThread().getName()+System.currentTimeMillis());
                  Thread.sleep(10000);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.readLock().unlock();
              }
          }
          public void write()
          {
              try {
                  lock.writeLock().lock();
                  System.out.println("获得写锁"+Thread.currentThread().getName()+System.currentTimeMillis());
                  Thread.sleep(10000);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }finally {
                  lock.writeLock().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
    2. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadA3 类

      package reentrantreadwritelock.thread;
      
      
      import reentrantreadwritelock.service.MyService3;
      
      public class ThreadA3 extends Thread{
          private MyService3 myService = new MyService3();
          public ThreadA3(MyService3 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.read();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    3. 创建一个reentrantreadwritelock.thread包,在包下创建ThreadB3 类

      package reentrantreadwritelock.thread;
      
      import reentrantreadwritelock.service.MyService3;
      
      public class ThreadB3 extends Thread{
          private MyService3 myService = new MyService3();
          public ThreadB3(MyService3 myService)
          {
              this.myService = myService;
          }
      
          @Override
          public void run() {
              myService.write();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    4. 创建一个reentrantreadwritelock.test包,在包下创建Run3 类

      package reentrantreadwritelock.test;
      
      import reentrantreadwritelock.service.MyService3;
      import reentrantreadwritelock.thread.ThreadA3;
      import reentrantreadwritelock.thread.ThreadB3;
      
      public class Run3 {
          public static void main(String[] args) {
              MyService3 myService =new MyService3();
              ThreadA3 a = new ThreadA3(myService);
              a.setName("a");
              a.start();
              ThreadB3 b = new ThreadB3(myService);
              b.setName("b");
              b.start();
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

      即创建一个MyService3实例,2个线程传入同一个MyService3实例,线程A调用MyService3的read方法,线程B调用MyService3的write方法,此时读写互斥,需要等第一个线程执行读方法结束后,第二个线程才能执行写方法内的任务。

    4.写读互斥

    即跟读写互斥相同,线程AB调用的方法互换即可。

    总结

    1. 使用Condition对象,Condition对象是跟ReentrantLock对象绑定的。Condition有await和signal、signalAll方法,通过awiat方法使线程进入等待状态,signal唤醒线程,signalAll唤醒全部线程。
    2. 通知部分线程时,可以使用创建多个Condition实例,通过调用指定的Condition对象的signal/signalAll方法唤醒指定部分的线程。
    3. 实现生产/消费模式多对多时,需要使用while来判断,并且唤醒方法用signalAll,还有就是用多个线程实例来操作这个生产和消费的方法。生产和消费的方法使用同步锁lock。
    4. 公平锁采用先到先得的策略,每次获取锁之前都会检查队列有没有排队等待的线程,没有才会尝试获取锁,如果有就将当前线程追加到队列中。
      非公平锁,采用“有机会插队”的策略,一个线程获取锁之前,要先尝试获取锁,而不是在队列中等待;如果获取锁没有成功,那么将自身追加到队列中进行等待。即比公平锁多了一个线程未入队时的尝试获取锁操作(插队操作)。
      公平锁在进入方法后,就会申请锁资源,查看等待队列中是否有线程等待,有则加入该等待队列,没有则获取锁资源。
    5. ReentrantLock具有完全互斥排他的特点,同一时间只有一个县城在执行ReentrantLock.lock()方法后面的任务,保证了同时写实例变量的线程安全性,但效率非常低下。
      ReentrantReadWriteLock读写锁,可以在同时进行读操作时不需要同步执行,提升了运行速度,加快运行效率。
      ReentrantLock和ReentrantReadWriteLock之间没有继承关系。
      读写锁表示有2个锁,一个是读操作相关的锁,也叫共享锁,另一个是写操作相关的锁,也叫排它锁。读锁之间不互斥,读锁和写锁与写锁和写锁之间互斥。即出现写锁就出现互斥同步的效果。
    6. Lock对象比synchronized关键具有更多更细的方法,Lock对象是synchronized关键字的进阶。
      并发包中,大量的的类使用了Lock接口作为同步的处理方式。
  • 相关阅读:
    Springboot整合MyBatisPlus swagger测试
    转转集团宣布品牌焕新背后:黄炜继续模仿爱回收,公司多次被处罚
    软件架构设计
    Date常用格式转换
    动态sql和分页
    Maven添加SQLserver的依赖及驱动
    [MySQL] 表的增删查改(CURD)
    C++:扫描线算法​(附完整源码)
    java正则表达式
    面试了一个34岁的java老码农年薪50w的面试题基本都能答得上
  • 原文地址:https://blog.csdn.net/weixin_49076273/article/details/127982828