ReentrantReadWriteLock学习分享

闲聊

大学的时候是有粗略的学了ReentrantReadWriteLock,大概记了下相关知识点(都是为了应付面试),实际没用过几次。

工作之后,我们的项目中很少用到,所以基本上相关的知识都忘了。

直到有天老同学问我ReentrantReadWriteLock相关的问题。他写了一段代码,用到了ReentrantReadWriteLock,但是一直获取不到读锁,让我帮忙看看。我就带着疑问重新学习了下。我觉得这样学习的效率挺高,而且印象深刻。

ReentrantReadWriteLock

意思是可重入读写锁。包含了两个意思,可重入,可获得读锁和写锁。

读写锁

多个线程读取共享数据(比如数据库)时,需要使用锁来确保数据的读取和写入的正确性,所以需要加锁。

很简单的道理就是,我在读取数据的时候你不能写;我在写的时候你不能读取也不能写;读写互斥,写写互斥;没有人在写的时候,大家都可以读取数据,读读不互斥。

读锁

没有线程在写的时候,大家都可以读取数据。

即没有写锁的情况下,多个线程都可以获取读锁,所以读锁是共享锁。

线程在读取数据时获取读锁意思就是告诉其他线程【我正在读取数据,你等下再写入吧】。

线程读取完数据后释放读锁。

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        readLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "开始读取");

        //  模拟读取操作

        readLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "读取完毕");
    }
}

线程Thread-0开始读取
线程Thread-0读取完毕

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        readLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "开始读取");

        //  模拟读取操作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        readLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "读取完毕");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();

        Thread threadB = new Thread(myThread);
        threadB.start();
    }
}
线程Thread-0开始读取
线程Thread-1开始读取
线程Thread-0读取完毕
线程Thread-1读取完毕

写锁

我想要写入某个数据,要先确保这个数据没有人在读,也没有人在写,我才能去写数据。

即当其他线程没有读锁和写锁的时候,才能获取写锁。写锁是互斥锁。

线程写入完数据后,释放写锁。

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        writeLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "开始写入");

        try {
            System.out.println(Thread.currentThread().getName() + " 写入数据");
        } finally {
            writeLock.unlock();
            System.out.println("线程" + Thread.currentThread().getName() + "结束写入");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();
    }
}
线程Thread-0开始写入
Thread-0 写入数据
线程Thread-0结束写入

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        writeLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "开始写入");

        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " 写入数据");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            writeLock.unlock();
            System.out.println("线程" + Thread.currentThread().getName() + "结束写入");
        }
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();

        Thread threadB = new Thread(myThread);
        threadB.start();
    }
}
线程Thread-1开始写入
Thread-1 写入数据
线程Thread-1结束写入
线程Thread-0开始写入
Thread-0 写入数据
线程Thread-0结束写入

可重入

即可重复获取锁。比如:

当前线程已经获取了读锁,它可以再获取一次读锁;

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        readLock.lock();
        System.out.println(Thread.currentThread().getName() + "得到了读锁");
        readLock.lock();
        System.out.println(Thread.currentThread().getName() + "又得到了读锁");
        readLock.unlock();
        System.out.println(Thread.currentThread().getName() + "释放了一个读锁");
        readLock.unlock();
        System.out.println(Thread.currentThread().getName() + "释放了最后一个读锁");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();
    }
}
Thread-0得到了读锁
Thread-0又得到了读锁
Thread-0释放了一个读锁
Thread-0释放了最后一个读锁

当前线程已经获取了写锁,它可以再获取一次写锁。

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        writeLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "得到了写锁");
        writeLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "又得到了写锁");

        writeLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "释放了写锁");
        writeLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "再次释放了写锁");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();
    }
}
线程Thread-0得到了写锁
线程Thread-0又得到了写锁
线程Thread-0释放了写锁
线程Thread-0再次释放了写锁

锁降级

锁降级的意思:该线程在获取了写锁之后,写锁未释放之前,可以获取读锁。

这是让线程写完之后能读取到正确的数据。保证数据的可读性,不会读到脏数据。

public class MyThread implements Runnable{

    static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
    static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

    @Override
    public void run() {
        writeLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "得到了写锁");
        readLock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "得到了读锁");
        writeLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "释放了写锁");
        readLock.unlock();
        System.out.println("线程" + Thread.currentThread().getName() + "释放了读锁");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread threadA = new Thread(myThread);
        threadA.start();
    }
}
线程Thread-0得到了写锁
线程Thread-0得到了读锁
线程Thread-0释放了写锁
线程Thread-0释放了读锁

在获取写锁之后,写入数据,再获取读锁,这时写锁还是在的。

需要先把写锁释放了,然后再读取操作完成之后再释放读锁。

总结

读写锁互斥,读锁与读锁不互斥,写锁与写锁互斥。

对于同一个线程,读写锁是可以重复获取的。第二次获取相同的锁时,第一个锁还是存在的,需要释放。

锁降级(在获取写锁后可以再去获得读锁):在获取读锁之后,要优先释放写锁。

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值