不懂并发?看完就超神!!!Java并发基础(四)

JUC

fastfail

快速失败机制是Java集合一种错误检测机制,在多线程下使用线程不安全的集合,每次在修改集合时都会将modCount++,而在遍历集合是会检查mod。
在这里插入图片描述

AQS 重点

Abstract Queued Synchronizer,阻塞式锁和相关同步工具的框架,用来构建锁和同步器的,AQS底层使用模板模式,Reentrantlock,semaphore等都都需要重写指定的方法。

isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

AQS将每一个请求资源的线程封装成为一个CLH锁队列的一个Node,根据Node来进行分配。
在这里插入图片描述
AQS定义了两种资源共享方式:

  1. Exclusive:独占,只能一个线程执行,ReentrantLock。
  2. Share:共享,可以多个线程同时执行,Semaphore,countdonwLatch,cyclicbarrier。

其中AQS定义了使用volatile修饰的state来表示资源的状态,如0表示没有被占用,1表示已被占用,线程通过使用CAS方式来修改state。常用方法:getstate、setstate。

reentrantlock

在这里插入图片描述

源码阅读警告

尽量配和视频来阅读。参考课程:全面深入学习java并发编程,java基础进阶中级必会教程

nonfairsync

使用空参构造器时,使用NofairSync。
在这里插入图片描述

加锁流程

调用lock方法,lock首先使用cas判断是否能将0更新为1,既1表示锁已经被1个线程获取,0表示没有线程获取这个锁,如果成功了调用setexxx方法相设置为owner。
在这里插入图片描述
在这里插入图片描述
失败了就表示此时的stat已经不为0了,为false,调用acquire方法,tryacquire会重试再使用CAS,如果为真,加有!整个为假,后面的语句就不会执行了,当为假时,整个为真,那么后面的语句就会执行,acquiredqueued会创建一个新的节点加入到等待队列中。
在这里插入图片描述
在这里插入图片描述
acquiredqueued会在一个for的死循环中不断尝试获取锁,第一个if,会先判断前驱节点是否为头节点,如果为头节点就会再次尝试获取锁。

如果获取失败了,就调用shouldparkafterfailedacquire,shouldparkafterfailedacquire会将前驱的node的state改为-1(表示下次要唤醒我)。

还会判断是否应该park,如果为true就表示前驱节点的status已经为-1了,该park掉了。如果为false就还会去进行下一轮的尝试。

也就是当thread1第一次竞争时,将前驱节点设为-1,同时返回false然后又去尝试一边,当又读取到shouldparkafterfailedacquire时,由于已经为-1了,返回true。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
多个线程重复竞争后:
在这里插入图片描述
如果尝试加锁成功了(没有其他竞争下):
在这里插入图片描述
在这里插入图片描述
如果在竞争下且失败了:
在这里插入图片描述

释放锁流程

在这里插入图片描述
在这里插入图片描述
如果头节点不为null且不为0,就会唤醒thread1,此时thread1就会从acquiredqueued的park开始重新tryacquire,此时获取到后就会将成为ownerthread,此时将thread1的这个node作为头节点,然后将原来的头节点去除掉(node就是一个装载thread的载体)。
在这里插入图片描述
在这里插入图片描述

非公平锁体现

假如thread1去tryacquire时,有其他线程来竞争。
在这里插入图片描述
在这里插入图片描述

可重入锁

当锁重入时就将state++,解锁时state–。
在这里插入图片描述
在这里插入图片描述

不可打断

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

公平锁

在这里插入图片描述
在这里插入图片描述

await

一开始thread0是owner,调用await就会进入conditionbojectwaiter流程,创建新的node状态为-2,然后将thread放入node,加入waiter队列。
在这里插入图片描述
在这里插入图片描述
然后就会进入AQS的fullyrelease释放锁,fullyrelease可以将锁重入的状态一次性减为0,同时会唤醒下一node,此时node就会按照公平锁或者非公平锁去竞争owner。
在这里插入图片描述
在这里插入图片描述

signal

只有owner才有资格调用,而且每次都是唤醒first node的线程既thread0。
在这里插入图片描述

在这里插入图片描述
如果有线程就唤醒进入dosignal,首先会将thread0从waiter队列中分离出来,然后执行transferforsignal将其放入aqs的队尾并将thread3的state改为-1,thread0改为0。

如果唤醒失败就又会去firstwaiter中寻找下一对象继续进行循环。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

reentrantreadwritelock

目的是让读读可以并发,让读写、写写互斥。

  1. 可重入锁。
  2. 读写分离。

在这里插入图片描述

源码

写锁的加锁流程

读写锁用的是同一个sync同步器。

t1加write锁:state稍有变化,由于读写锁一起使用,state被被分为两部分,写锁占用低16位,读锁占用高16位。
在这里插入图片描述
写锁的lock方法,调用aqs的acquire。
在这里插入图片描述
子类重写aqs的acquire,tryacquire方法尝试加锁。此时如果tryacquire返回的false加锁失败了,就执行AQS的acuqire方法。
在这里插入图片描述
———————————————————————————————
在这里插入图片描述
当c不等于0时:表示sync已经被写或者读或者读写加锁了 ,w是取得写锁部分,如果w=0那么表示已经加得锁是读锁,线程是调用写锁得lock,读写互斥就返回false。如果w不等于0,还会 current != getExclusiveOwnerThread() 判断是否是同一线程,表示使用的是写锁且是同一线程,即为锁重入。
在这里插入图片描述
当c等于0时:sync还没加锁 writerShouldBlock()看是创建的fair锁还是unfair锁。
在这里插入图片描述
如果为unfaire锁:写锁可以永远barge。
在这里插入图片描述
如果为fair:还会去判断等待队列中是否有等待的。
在这里插入图片描述
如果返回false,就执行!compareAndSetState(c, c + acquires))方法去修改状态,如果返回true加锁成功,就执行setExclusiveOwnerThread(current)将本线程设为独占的owner并return true,如果返回false加锁失败,就执行return false。
———————————————————————————————
读锁的加锁流程

与写锁不同的是调用的sync.acquireShared(1);然后调用tryAcquireShared(arg)

acquire任然是AQS的acquire。
在这里插入图片描述
在这里插入图片描述
在读写锁下只有-1和0。
在这里插入图片描述
———————————————————————————————
在这里插入图片描述
———————————————————————————————
写锁解锁流程
在这里插入图片描述
在这里插入图片描述
将state-1后判断写锁部分是否为0,如果不为0就表示发生了重入free为false,如果为0就将owner变为null然后更新state,返回free。
在这里插入图片描述
———————————————————————————————

semaphore

Semaphore(信号量)-允许多个线程同时访问: synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。

acquire
在这里插入图片描述
permits为3,每次线程占用permits,就将state-1。
在这里插入图片描述
当permits大于0时就加锁成功,而加入返回的remaining小于0,就执行doacquiresharedinteruptibly。
在这里插入图片描述
获取到state,使用当前剩下的permits减去需要的,如果大于0,将permit更新,然后返回remaining,如果小于0了就直接返回remaining。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
release
在这里插入图片描述
在这里插入图片描述
当tryrelease返回会true时,就执行doreleaseshared。
在这里插入图片描述
先获取state既当前剩下的permits,然后加上1,然后进行cas并返回true。
在这里插入图片描述
signal为-1,判断头节点的state是不是-1,是-1表明后面有节点需要unpark,首先会调用cas将-1改为0,成功就unpark后继节点,这时这个节点的线程就会从doacquire开始for循环。
在这里插入图片描述
thread0拿到owner后

在这里插入图片描述

countdonwlatch

用来进行线程同步协作,让一个线程等待其他所有线程完成倒计时后才开始运行。
await等待计数归为0,countdown让计数减一。
在这里插入图片描述

cyclicbarrier

CyclicBarrier(循环栅栏): CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。

CyclicBarrier 默认的构造方法是 CyclicBarrier(int parties),parties表示屏障拦截的线程数量,每个线程调用 await()方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞,直到线程数达到parties就继续运行。
由于countdownlatch其实不能被重复使用,而cyclic可以重复使用。

使用案列

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值