ReentrantLock和ReentrantReadWriteLock对比

ReentrantLock和ReentrantReadWriteLock对比

作者:追梦1819
原文:https://blog.csdn.net/weixin_39759846/article/details/90239241
版权声明:本文为博主原创文章,转载请附上博文链接!

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)。

猜你喜欢

转载自blog.csdn.net/weixin_39759846/article/details/90239241