Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)

                                                   ReentrantReadWriteLock

                                                                                            个人博客:www.xiaobeigua.icu 


1.3 ReentrantReadWriteLock 读写锁

1.3.1认识读写锁

        synchronized 内部锁与 ReentrantLock 锁都是独占锁(排它锁), 同一 时间只允许一个线程执行同步代码块,可以保证线程的安全性,但是执行效率低。

         ReentrantReadWriteLock 读写锁 是一种改进的排他锁,也可以称作 共享/排他锁。允许多个线程同时读取共享数据,但是一次只允许一个线程对共享数据进行更新。

         读写锁通过读锁与写锁来完成读写操作,线程在读取共享数据前必须先持有读锁,该读锁可以同时被多个线程持有,即它是共享的。

        线程在修改共享数据前必须先持有写锁,写锁是排他的,一个线程持有写锁时其他线程无法获得相应的锁,读锁只是在读线程之间共享,任何一个线程持有读锁时,其他线程都无法获得写锁, 保证线程在读取数据期间没有其他线程对数据进行更新,使得读线程能够读到数据的最新值,保证在读数据期间共享变量不被修改,这也保证了数据共享之间的可见性。

                                      Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第1张图片

锁类型 获得条件 排他性 作用
读锁 写锁未被任意线程持有 对读线程是共享的,对写线程是排他的 允许多个读线程可以同时读 取共享数据,保证在读共享数 据时,没有其他线程对共享数 据进行修改
写锁 该写锁未被其他线程持 有,并且相应的读锁也未被其他线程持有 对读线程或者写线程都是排他的 保证写线程以独占的方式修 改共享数据

        读写锁允许 读读共享读写互斥,写写互斥

        在java.util.concurrent.locks包中定义了ReadWriteLock接口,该接口中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁。该接口的实现类是 ReentrantReadWriteLock。

        注意 readLock()与 writeLock()方法返回的锁对象是同一个锁的两个不同的角色,就像作者本人人在学校里是学生,下对象面前就是威猛先生(手动狗头),不是分别获得两个不同的锁。

        锁 ReadWriteLock 接口实例可以充当两个角色。

        读写锁的其他使用方法:              

//定义读写锁
ReadWriteLock rwLock = new ReentrantReadWriteLock();

//获得读锁 
Lock readLock = rwLock.readLock();

//获得写锁 
Lock writeLock = rwLock.writeLock();



//读数据
try{
    readLock.lock(); //申请读锁
    读取共享数据
}finally{
    readLock.unlock(); //总是在 finally 子句中释放锁
}

//写数据
try{
    writeLock.lock(); //申请写锁
    更新修改共享数据
}finally{
    writeLock.unlock(); //总是在 finally 子句中释放锁
}

1.3.2  读读共享

        ReadWriteLock 读写锁可以实现多个线程同时读取共享数据,即读 读共享,可以提高程序的读取数据的效率。

例子:创建5个线程来获取读锁,线程获取之后,未释放锁,看看其它线程获取情况

public class TestOne {

    //创建读写锁
    static  ReadWriteLock readWriteLock=  new ReentrantReadWriteLock();
    //创建读锁
    static  Lock readLock = readWriteLock.readLock();

    public static void main(String[] args) {
        TestOne t1=new TestOne();
        //创建5个线程使用锁
        for (int i=0;i<5;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    t1.read();
                }
            }).start();
        }

    }

    //创建方法 供线程调用
    public void read(){
        
        try {
            //申请读锁
            readLock.lock();
            System.out.println(Thread.currentThread().getName()+"获得读锁");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程获得锁未释放锁
        }


    }
}

结果:

Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第2张图片

 结论:在一个线程获取锁未释放的条件下,其他线程还可以继续获取锁,表明了,读锁在读线程之间是共享的,提高了数据的读取效率。

1.3.3 写写互斥

         通过 ReadWriteLock 读写锁中的写锁,只允许有一个线程执行 lock() 后面的代码。

例子:创建两个线程来争夺写锁,看看一个线程获得锁后,不释放  看另一个锁的情况

public class TestOne {
    //创建读写锁
   static ReadWriteLock readWriteLock=  new ReentrantReadWriteLock();
    //创建写锁
   static Lock writeLock =readWriteLock.writeLock();

    public static void main(String[] args) {


        TestOne t1=new TestOne();
        //创建2个线程使用锁
        for (int i=0;i<2;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    t1.write();
                }
            }).start();
        }

    }

    //创建方法 供线程调用
    public void write(){
        try {
            //申请写锁
            System.out.println(Thread.currentThread().getName()+"尝试获取写锁...");
            writeLock.lock();
            System.out.println(Thread.currentThread().getName()+"获得写锁成功,开始修改数据");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程获得写锁 不释放
        }


    }
}

结果:

        Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第3张图片

 结论:在一个线程获得写锁后,只要不释放锁,别的线程就要一直等待,这说明了写锁是互斥的,这也保证了数据共享的原子性

1.3.4 读写互斥

        写锁是独占锁,是排他锁,所以读线程与写线程也是互斥的,有写锁被线程占有,那么别的锁就不可能被别的线程占有

例子:创建3 个线程 一个线程先获取写锁,两个线程尝试获取读锁 看情况

public class TestOne {
    //创建读写锁
    static ReadWriteLock readWriteLock=  new ReentrantReadWriteLock();
    //创建读锁
    static Lock readLock=readWriteLock.readLock();
    //创建写锁
    static Lock writeLock =readWriteLock.writeLock();

    public static void main(String[] args) throws InterruptedException {

        TestOne t1=new TestOne();
        //创建1个线程使用写锁,不释放
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.write();
            }
        }).start();
        //main线程休眠1s确保 线程获得写锁
        Thread.sleep(1000);

        //创建2个线程尝试获得读锁
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.read();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.read();
            }
        }).start();


    }

    //创建写锁方法 供线程调用
    public void write(){
        try {
            //申读读锁
            System.out.println(Thread.currentThread().getName()+"尝试获取写锁...");
            writeLock.lock();
            System.out.println(Thread.currentThread().getName()+"获得写锁成功,开始修改数据");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //writeLock.unlock();
            //线程获得读锁 不释放
        }
    }

    //创建都锁方法
    public void read(){
        try {
            //申请都锁
            System.out.println(Thread.currentThread().getName()+"尝试获取读锁...");
            readLock.lock();
            System.out.println(Thread.currentThread().getName()+"获得读锁成功,开始读取数据");
            } catch (Exception e) {
                e.printStackTrace();
        } finally {

             //线程获得写锁 不释放
        }
    }


}

 结果:线程0获取写锁后未释放,其他线程都处于等待状态,无法获取读锁

        Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第4张图片

如果获取写锁了之后 再释放了它,看其他线程的情况:

Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第5张图片

 结果:写锁被释放后,其他两个线程可以获取读锁

Java多线程-10 (Lock锁之ReentrantReadWriteLock 读写锁)_第6张图片

 结论:

        可以看出在一个线程获取到写锁后,未释放写锁之前,其他线程也是无法获取读锁的,因为写锁是排他锁,就和synchronized () 和可重入锁一样的效果。也是未来共享数据的原子性。

你可能感兴趣的:(多线程,多线程,Lock,读写锁,ReadWriteLock,锁)