Lock接口读写锁原理介绍

一、线程间通信的方式

首先,介绍以下几种线程间的通信方式:

协作方式死锁方式1(通过主动加把锁)死锁方式1(先唤醒再挂起)备注
suspend/resume死锁死锁弃用
wait/notify不死锁死锁只能在synchronized关键字中用,基于对象监视器
park/unPark死锁不死锁用LockSupport,令牌机制
condition的await/signal不死锁死锁只能在ReentrantLock中用

二、Synchronized与Lock的对比

(1)Synchronized优点:使用简单;由JVM提供,包括了多种优化方式(锁升级,具体可看我Synchronized原理的博客);锁的释放不用人工干预,由JVM释放,降低了死锁的可能性。
(2)Synchronized缺点:无法使用锁的高级功能,如读写锁等,使用不灵活。
(3)Lock优点:所有Synchronized的缺点,Lock都可以解决。
(4)Lock缺点:需要手动释放锁调用unLock()方法,使用不当可能造成死锁。

三、Lock接口读写锁原理介绍

读写锁维护了一对关联锁,读锁只用于读操作,可同时被多个线程持有;写锁只用于写操作,同时只能被一个线程持有;并且在同一时间读锁和写锁也不能被同时持有,它适合读多写少的情况。读写锁的API如下:

public class ReadWriteLockTest {
    volatile long i = 0;
    //读写锁
    ReadWriteLock rwLock = new ReentrantReadWriteLock();
    //读方法
    public void read() {
        rwLock.readLock().lock();//加读锁
        long iValue = i;
        rwLock.readLock().unlock();//释放读锁
    }
    //写方法
    public void write() {
        rwLock.writeLock().lock();//加写锁
        i++;
        rwLock.writeLock().unlock();//加写锁
    }
    public static void main(String[] args) throws InterruptedException {
        final ReadWriteLockTest demo = new ReadWriteLockTest();
        long startTme = System.currentTimeMillis();
        for (int i=0;i<=30; i++){
            int n = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    long starttime = System.currentTimeMillis();
                    for (int j=0; j<400000; j++){
                        if (n==0) demo.write();//写少
                        else demo.read();//读多
                    }
                }
            }).start();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"+ (endTime - startTme));
    }
}

读写锁的原理图如下:
在这里插入图片描述
读写锁中包含了四个部分:
(1)watiers:等待队列,没有抢到读/写锁的线程被放入此队列等待抢锁;
(2)owner:标记写锁是被那个线程持有的,同一时间只能有一个线程持有写锁;
(3)writeCount:标记写锁(被同一个线程)重入的次数;
(4)readCount:标记读锁(共享锁)被几个线程占用。
由于动图不会搞━━( ̄ー ̄*|||━━,接下来我来用文字讲解读写锁的争抢过程:
<1>线程t1来抢“写锁”,首先判断readCount是否为0(因为读/写锁互斥,不为0说明读锁被占了,t1不能抢写锁),若readCount=0,则t1可以抢写锁,t1抢到了写锁,对writeCount做CAS操作加1,此时writeCount=1,owner=t1;
<2>同样的,t1又去抢“写锁”,先判断readCount是否为0,再判断writeCount是否为0,目前writeCount=1,owner=t1,因为owner=t1,所以t1又抢到了写锁,此时writeCount=2,owner=t1;
<3>t2线程来抢“写锁”,先判断readCount是否为0,再判断writeCount是否为0,目前writeCount=2,owner=t1,t2抢不到写锁,t2进入watiers等待队列;
<4>t1释放一次写锁,此时writeCount=1,owner=t1,再释放一次写锁,此时writeCount=0,owner=null,那么此时在等待队列中的t2就能抢到写锁,此时writeCount=1,owner=t2;
<5>t3线程来抢“读锁”,先判断writeCount是否为0,此时writeCount=1,即t2占用了写锁,而读锁与写锁互斥,所以t3抢不到读锁,t3进入等待队列;
<6>同样的t4,t5也来抢“读锁”,都抢不到,t4,t5进入等待队列;
<7>t2释放了写锁,此时writeCount=0,owner=null,t3被唤醒,出等待队列,重新去抢“读锁”,先判断writeCount是否为0,发现writeCount=0,抢成功读锁,对readCount做CAS操作加1,此时readCount=1;
<8>t4,t5经历同样的操作,此时readCount=3;
<9>此时t6线程又来抢“写锁”,发现readCount=3,抢写锁失败,进入等待队列;
<10>当t3,t4,t5都释放了读锁,此时readCount=0,等待队列中的t6线程就又能来抢写锁了。
以上就是我用文字描述的读写锁争抢的全过程,不止各位看官是否看懂了呢︿( ̄︶ ̄)︿

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java中的Lock接口提供了比synchronized关键字更加灵活和强大的线程同步机制。下面是Lock接口的常用方法: 1. lock():获取锁,如果锁已经被其他线程占用,则当前线程会被阻塞。 2. tryLock():尝试获取锁,如果锁没有被其他线程占用,则获取锁成功并返回true;否则返回false,不会阻塞当前线程。 3. tryLock(long time, TimeUnit unit):在指定时间内尝试获取锁,如果在指定时间内获取到锁,则返回true;否则返回false,不会阻塞当前线程。 4. unlock():释放锁,如果当前线程持有锁,则释放锁;否则会抛出IllegalMonitorStateException异常。 Lock接口的实现类包括ReentrantLock、ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock等。其中ReentrantLock是最基本的实现类,而ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock是对读写锁的支持。 使用Lock接口进行线程同步时,需要注意以下几点: 1. 获取锁后必须在finally块中释放锁,否则可能导致死锁。 2. 不要使用Lock接口代替synchronized关键字进行简单的线程同步,因为Lock接口需要手动释放锁,容易出错。 3. 在使用ReentrantLock实现线程同步时,如果获取锁的线程出现异常而没有释放锁,则可能会导致其他线程一直阻塞,从而产生死锁。为了避免这种情况,建议使用try-finally块释放锁,或者使用Lock接口提供的unlock()方法释放锁。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenzihao36

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值