zoukankan      html  css  js  c++  java
  • ReentrantLock和ReentrantReadWriteLock对比

    **本文系作者原创,转载请注明:https://www.cnblogs.com/yanfei1819/p/10314533.html **

    ReentrantLock

    一、简介

    ReentrantLock重入锁和synchronize关键字一样,是互斥锁。比synchronize关键字更加灵活。

    二、基本方法

    三、与synchronize对比

    1. demo演示

      线程不安全:

      public class WriteAndReadThread {
          public static void main(String[] args) {
              Person person = new Person();
              Thread t1 = new Output(person);
              Thread t2 = new Input(person);
              t1.start();
              t2.start();
          }
      }
      // 写数据的线程
      class Output extends Thread {
          private Person person;
          public Output(Person person) {
              this.person = person;
          }
          @Override
          public void run() {
              int count = 0;
              while (true) {
                  if (count == 0) {
                      person.name = "小明";
                      person.gender = "男";
                  } else {
                      person.name = "小红";
                      person.gender = "女";
                  }
                  count = (count + 1) % 2; // 奇数偶数轮流展现
                  person.flag = true;
              }
          }
      }
      // 读数据的线程
      class Input extends Thread {
          private Person person;
          public Input(Person person) {
              this.person = person;
          }
          @Override
          public void run() {
              while (true) {
                  System.out.println(person.name + "," + person.gender);
              }
          }
      }
      class Person {
          public String name;
          public String gender;
          public boolean flag = false;
      }
      

      以上代码没有考虑线程安全问题,读写线程会竞争共享资源,导致数据紊乱。

      1)线程安全(加synchronize关键字):

      public class ThreadTwo {
          public static void main(String[] args) {
              Person person = new Person();
              Thread t1 = new Output(person);
              Thread t2 = new Input(person);
              t1.start();
              t2.start();
          }
      }
      
      // 写的线程
      class Output extends Thread{
          private Person person;
          public Output(Person person){
              this.person=person;
          }
          @Override
          public void run(){
              int count = 0;
              while (true){
                  synchronized (person){
                      if(person.flag){
                          try {
                              person.wait(); // 唤醒等待的线程
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      if(count==0){
                          person.name="小明";
                          person.gender="男";
                      }else {
                          person.name="小红";
                          person.gender="女";
                      }
                      count=(count+1)%2;
                      person.flag=true;
                      person.notify();
                  }
              }
          }
      }
      // 读数据的线程
      class Input extends Thread{
          private Person person;
          public Input(Person person){
              this.person=person;
          }
          @Override
          public void run(){
              while (true){
                  synchronized (person){
                      if(!person.flag){
                          try {
                              person.wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.println(person.name+","+person.gender);
                      person.flag=false;
                      person.notify();
                  }
              }
          }
      }
      class Person{
          public String name;
          public String gender;
          public boolean flag=false;
      }
      

      2)线程安全(用Lock):

      public class LockDemo {
          public static void main(String[] args) {
              Person2 person2 = new Person2();
              Condition condition = person2.lock.newCondition();
              Input2 input2 = new Input2(person2,condition);
              Output2 output2 = new Output2(person2,condition);
              input2.start();
              output2.start();
          }
      }
      
      // 写的线程
      class Output2 extends Thread {
          private Person2 person;
          private Condition condition;
          public Output2(Person2 person, Condition condition) {
              this.person = person;
              this.condition = condition;
          }
          @Override
          public void run() {
              int count = 0;
              while (true) {
                  try {
                      person.lock.lock();
                      if (person.flag) {
                          try {
                              condition.await(); // 使线程休眠,作用等于synchronize中的thread.wait()方法
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      if (count == 0) {
                          person.name = "小明";
                          person.gender = "男";
                      } else {
                          person.name = "小红";
                          person.gender = "女";
                      }
                      count = (count + 1) % 2;
                      person.flag = true;
                      condition.signal();
                  }catch (Exception e){
                  }finally {
                      person.lock.unlock(); // 必须手动关闭线程
                  }
              }
          }
      }
      
      // 读的线程
      class Input2 extends Thread {
          private Person2 person;
          private Condition condition; // 该接口的作用是精确控制锁的行为
          public Input2(Person2 person, Condition condition) {
              this.person = person;
              this.condition = condition;
          }
      
          @Override
          public void run() {
              while (true) {
                  try {
                      person.lock.lock();
                      if (!person.flag) {
                          try {
                              condition.await();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                      System.out.println(person.name + "," + person.gender);
                      person.flag = false;
                      condition.signal();//唤醒线程
                  }catch (Exception e){
      
                  }finally {
                      person.lock.unlock();
                  }
              }
          }
      }
      
      class Person2 {
          public String name;
          public String gender;
          public boolean flag = false; // 该属性是用来控制线程之间的通讯
          Lock lock = new ReentrantLock();
      }
      
    2. 总结

      相同点:

      ​ 都是用于线程同步锁,都是互斥锁。

      不同点:

      ​ 1.如果用汽车来类比,synchronize相当于自动挡,Lock相当于手动挡。即:synchronize是内置锁,只要加上synchronize的代码的地方开始,代码结束的地方自动释放资源。lock必须手动加锁,手动释放资源。

      ​ 2.synchronize优点是代码量少,自动化。缺点是扩展性低,不够灵活。

      ​ 3.Lock优点是扩展性好,灵活。缺点是代码量相对稍多。

      ​ 4.释放锁的情况:

      ​ synchronize:1)线程执行完毕;2)线程发生异常;3)线程进入休眠状态。

      ​ Lock:通过unLock()方法。

      注意点:

      ​ 1.wait()和notify()/notifyAll()必须出现在synchronize修饰的代码块中;

      ​ 2.资源的释放通常放在finally中;

      ​ 3.最好不要将lock()方法写在try{}中,因为如果 发生异常的话,抛出异常,同时锁资源无法释放。

    ReentrantReadWriteLock

    一、简介

    ​ ReentrantLock虽然可以灵活地实现线程安全,但是他是一种完全互斥锁,即某一时刻永远只允许一个线程访问共享资源,不管是读数据的线程还是写数据的线程。这导致的结果就是,效率低下。

    ​ ReentrantReadWriteLock类的出现很好的解决了该问题。该类实现了ReadWriteLock接口,而ReadWriteLock接口中维护了两个锁:读锁(共享锁)和写锁(排他锁)。

    public interface ReadWriteLock {
        Lock readLock();
        Lock writeLock();
    }
    

    二、基本方法

    三、使用

    ​ ReentrantReadWriteLock中维护了读锁和写锁。允许线程同时读取共享资源;但是如果有一个线程是写数据,那么其他线程就不能去读写该资源。即会出现三种情况:读读共享,写写互斥,读写互斥。

    以下以代码演示:

            ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
            Lock readLock = reentrantReadWriteLock.readLock();
            Lock writeLock = reentrantReadWriteLock.readLock();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // writeLock.lock(); // 写锁加锁
                    readLock.lock(); // 读锁加锁
                    try {
                        for (int i = 0; i < 10; i++) {
                            System.out.println("我是第一个线程,线程名是"+Thread.currentThread().getName()+",当前时间是"+System.currentTimeMillis());
                        }
                        Thread.sleep(2000); // 休眠2s
                    }catch (Exception e){
                    }finally {
                        // writeLock.unlock();// 写锁释放锁
                        readLock.unlock();// 读锁释放锁
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // writeLock.lock(); // 写锁加锁
                    readLock.lock();// 读锁加锁
                    try {
                        for (int i = 0; i < 10; i++) {
                            System.out.println("我是第二个线程,线程名是"+Thread.currentThread().getName()+",当前时间是"+System.currentTimeMillis());
                        }
                    }catch (Exception e){
                    }finally {
                        // writeLock.unlock();// 写锁释放锁
                        readLock.unlock();
                    }
                }
            }).start();
    

    以上两个线程如果是读锁,则会同时执行(打印的时间几乎相等),但是如果是写锁,则不会同时执行(打印时间相差2s)。

  • 相关阅读:
    lodash-es 最小化引入
    shortid id生成器
    结构体声明的方式 及类namespace的前置声明
    结构体中使用 箭头 与 点 的区别
    进入Docker容器的几种方式
    协议分析处理工具ProtoBuf
    PubSub ——“发布/订阅”模式
    在Windows/linux下进行gdb调试
    C++中的域作用符::的作用
    C++ 中常用关键字及其用法
  • 原文地址:https://www.cnblogs.com/yanfei1819/p/10314533.html
Copyright © 2011-2022 走看看