关于lock:ReentrantLock

1.Synchronized和ReentrantLock的区别 ①ReentrantLock显示地取得,开释锁,synchronized隐式取得开释锁 ②ReentrantLock可响应中断,可轮回,synchronized是不能够响应中断的 ③ReentrantLock是API级别的,synchronized是JVM级别的 ④ReentrantLock能够实现偏心锁 ⑤ReentrantLock通过Condition能够绑定多个条件 ⑥底层实现不一样,synchronized是同步阻塞,应用的是乐观并发策略,lock是同步非阻塞,采纳的是乐观并发策略。 ⑦Lock是一个接口,而synchronized是java中的关键字,synchronized是内置的语言实现 ⑧synchronized 在产生异样时,会主动开释线程占有的锁,因而不会导致死锁景象产生;而 Lock 在产生异样时,如果没有被动通过 unLock()去开释锁,则很可能造成死锁景象, 因而应用 Lock 时须要在 finally 块中开释锁。 2.Synchronized底层如何实现?锁降级过程synchronized底层通过不同的锁实现。 线程获取共享资源时。 第一步:查看MarkWord外面是不是放的本人的ThreadId,如果是,示意以后线程是处于“偏差锁”。 第二步:如果MarkWord不是本人的ThreadId,锁降级,这时候,用CAS来执行切换,新的线程依据MarkWord里现有的ThreadId,告诉之前线程暂停,之前线程将Markword的内容置为空。 第三步:两个线程都把锁对象的HashCode复制到本人新建的用于存储锁的记录空间,接着开始通过CAS操作,把锁对象的MarkWord的内容批改为本人新建的记录空间的地址的形式竞争MarkWord。 第四步:第三步中胜利执行CAS的取得资源,失败的则进入自旋。 第五步,自旋的线程在自旋过程中,胜利取得资源(即之前取得资源的线程执行实现并开释了共享资源),则整个状态仍然处于轻量级锁的状态,如果自旋失败。 第六步,进入重量级锁的状态,这个时候,自旋的线程进行阻塞,期待之前线程执行实现并唤醒本人

February 28, 2023 · 1 min · jiezi

关于lock:ReentrantLock-的公平锁与非公平锁

偏心锁与非偏心锁是指,多个线程在获取同一把锁的策略,是依照先到先得还是间接竞争。先到先得的策略就是偏心锁,排队对所有的线程来说是偏心的,间接竞争的策略则是非偏心的。 在调用ReenterantLock的构造函数的时候决定是结构一个偏心锁还是非偏心锁。 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();}来看获取锁的过程: ReentrantLock lock = new ReentrantLock();lock.lock();点进去看,是调用了sync的lock办法这里就是对应了咱们的偏心锁/非偏心锁的lock办法。到这里没有什么大的差别,无非是非偏心锁多了一个步骤。cas操作去尝试获取锁,胜利了设置owner线程为以后线程。失败了再调用acquire办法获取锁。那接下来持续看两种锁的acquire办法,两个办法都来到了aqs的acquire办法。 点进去那些办法,所有办法都进入了aqs实现的步骤,只有tryAcquire办法有不同的实现。进去看,偏心锁只是多了 hasQueuedPredecessors这个办法。 偏心锁 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 以后锁的状态是无所状态 if (!hasQueuedPredecessors() && // 判断队列中是否有前驱节点,若果有前驱节点,就不会执行上面的代码,不会去获取锁 compareAndSetState(0, acquires)) { // 应用cas尝试获取锁 setExclusiveOwnerThread(current); // 设置owner线程为以后线程 return true; } } else if (current == getExclusiveOwnerThread()) { // 持有锁的线程为以后线程 int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); // 更新sate(重入锁的原理) return true; } return false;}以上剖析了偏心锁和非偏心锁的一个不同。上面以非偏心锁剖析加锁的过程获取锁的过程: ...

March 5, 2021 · 2 min · jiezi

关于lock:面试官你说说ReentrantLock和Synchronized区别

大家好!又和大家见面了。为了防止面试难堪,明天同比拟艰深语言和大家聊下ReentrantLock和Synchronized区别! 应用形式Synchronized能够润饰实例办法,静态方法,代码块。主动开释锁。 ReentrantLock个别须要try catch finally语句,在try中获取锁,在finally开释锁。须要手动开释锁。 实现形式Synchronized是重量级锁。重量级锁须要将线程从内核态和用户态来回切换。如:A线程切换到B线程,A线程须要保留以后现场,B线程切换也须要保留现场。这样做的毛病是消耗系统资源。 ReentrantLock是轻量级锁。采纳cas+volatile治理线程,不须要线程切换切换,获取锁线程感觉本人必定能胜利,这是一种乐观的思维(可能失败)。 用一个形象例子来阐明:比方您在看我这篇文章时,感觉“重量级锁”概念不是很明确,就立即去翻看对于“重量级锁”的其余文章,过会儿回头再持续往下面看, 这种行为咱们称为切换。保留现场的意思就是你大脑须要记住你跳跃的点而后持续浏览,如果文章篇幅大,你的大脑可能须要记忆越多的货色,会越消耗脑神经。同理,在轻量级锁中,你感觉“重量级锁”概念不是很明确,他不会立即去翻看其余文章,他会保持会儿持续看,如果切实不明确再去翻材料了。须要留神的是:这是两种不一样的思维形式,前者是被动阻塞乐观锁,状态是block,后者是被动的阻塞乐观锁,状态是wait。 偏心和非偏心Synchronized只有非偏心锁。 ReentrantLock提供偏心和非偏心两种锁,默认是非偏心的。偏心锁通过构造函数传递true示意。 用一个形象例子来阐明:排队打饭,Synchronized容许插队,如果ReentrantLock是偏心锁,就不许插队了。 可重入锁Synchronized和ReentrantLock都是可重入的,Synchronized是本地办法是C++实现,而ReentrantLock是JUC包用Java实现。 用一个形象例子来阐明:如下图:一个房中房,房里外各有一把锁,但只有惟一的钥匙能够开,领有钥匙的人能够先进入门1,再进入门2,其中进入门2就是叫锁可重入了。 在ReentrantLock中,重入次数用整形state示意。进入1次递增1次,进去1次递加1次。 可中断的Synchronized是不可中断的。 ReentrantLock提供可中断和不可中断两种形式。其中lockInterruptibly办法示意可中断,lock办法示意不可中断。 用一个形象例子来阐明:叫练和叫练女朋友一起去做核酸,叫练女朋友排在后面,所以叫练女朋友进门先做,叫练在门外排队期待过程中忽然接到领导电话要回去批改bug,叫练当初有两种抉择,1.不和女朋友打招呼,立刻回去批改bug,2.期待女朋友做完核酸,进去和女朋友打个招呼,而后回去批改bug。这两种状况最终都会导致一个后果,叫练无奈实现核酸,在这两种状况中,尽管叫练都被领导中断了,但第一种状况叫练立刻反馈领导叫可中断,第二种状况是叫练为了不做独身狗,打个招呼再去批改bug,须要留神的是“打招呼”须要提前获取锁,也就是须要期待叫练女朋友做完核酸检测。如果是你,遇到叫练这种状况,你会怎么办?期待你的回答!点关注,不迷路,我是叫练【公众号】,边叫边练。 条件队列Synchronized只有一个期待队列。 ReentrantLock中一把锁能够对应多个条件队列。通过newCondition示意。 用一个形象例子来阐明:母鸡下蛋和捡蛋人对应生产者和消费者,母鸡产蛋后,捡蛋人须要被母鸡告诉,母鸡产蛋过程中,其中捡蛋人就会入条件队列(期待队列)。捡蛋人捡蛋实现后,捡蛋人须要告诉母鸡持续产蛋,捡蛋人捡蛋过程中,母鸡也须要退出条件队列期待。 留神:有几个概念须要阐明下。同步队列,条件队列和期待队列。 同步队列:多线程同时竞争一把锁失败被挂起的线程。 条件队列:正在执行的线程调用await/wait,从同步队列退出的线程会进入条件队列。正在执行线程调用signal/signalAll/notify/notifyAll,会将条件队列一个线程或多个线程退出到同步队列。 期待队列:和条件队列一个概念。 总结明天咱们用通俗易懂的文字描述了ReentrantLock和Synchronized关系。喜爱的请点赞加评论哦!点关注,不迷路,我是叫练【公众号】,边叫边练。期待咱们下次再见!

January 26, 2021 · 1 min · jiezi

关于lock:核酸检测让我明白AQS原理

春节越来越近了,疫情也越来越重大,但挡不住叫练携一家老小回老家(湖北)团圆的激动。响应国家要求去咱们做核酸检测了。 独占锁早上叫练带着一家三口来到了南京市第一医院做核酸检测,护士小姐姐站在医院门口拦着通知咱们人比拟多,无论小孩儿小孩,须要排队一个个期待医生采集唾液检测,OK,上面咱们用代码+图看看咱们一家三口是怎么排队的! import java.util.concurrent.locks.ReentrantReadWriteLock;/** * @author :jiaolian * @date :Created in 2021-01-22 10:33 * @description:独占锁测试 * @modified By: * 公众号:叫练 */public class ExclusiveLockTest { private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); //医院 private static class Hospital { private String name; public Hospital(String name) { this.name = name; } //核酸检测排队测试 public void checkUp() { try { writeLock.lock(); System.out.println(Thread.currentThread().getName()+"正在做核酸检测"); //核酸过程...好受... Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } finally { writeLock.unlock(); } } } public static void main(String[] args) throws InterruptedException { Hospital hospital = new Hospital("南京市第一医院"); Thread JLWife = new Thread(()->hospital.checkUp(),"叫练妻"); JLWife.start(); //睡眠100毫秒是让一家三口是有程序的排队去检测 Thread.sleep(100); Thread JLSon = new Thread(()->hospital.checkUp(),"叫练子"); JLSon.start(); Thread.sleep(100); Thread JL = new Thread(()->hospital.checkUp(),"叫练"); JL.start(); }}如上代码:在主线程启动三个线程去医院门口排队,女士优先,叫练妻是排在最后面的,两头站的是叫练的孩子,最初就是叫练本人了。咱们假如模仿了下核酸检测一次须要3秒。代码中咱们用了独占锁,独占锁能够了解成医院只有一个医生,一个医生同时只能为一个人做核酸,所以须要一一排队检测,所以代码执行结束一共须要破费9秒,核酸检测就能够全副做完。代码逻辑还是比较简单,和咱们之前文章形容synchronized同理。核酸排队咱们用图形容下吧! ...

January 23, 2021 · 3 min · jiezi

关于lock:Python3-的进程池与锁qbit

前言测试环境Windows 10Python 3.8.2loguru 0.5.3Python 3.8 多过程官网文档: https://docs.python.org/3.8/l...loguru 0.5.3 不是多过程平安的,正好能够用来做多过程与锁的测试参考文章: Python sharing a lock between processes代码与阐明无锁多过程代码# encoding: utf-8# author: qbit# date: 2021-01-14# summary: 无锁多过程测试from multiprocessing import Poolfrom loguru import loggerdef ProcOne(ch): logger.info(ch*20) return chdef Main(): line = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' with Pool(4) as p: results = p.map(func=ProcOne, iterable=line) print(repr(results))if __name__ == "__main__": Main()运行后果(乱) multiprocessing.Lock代码# encoding: utf-8# author: qbit# date: 2021-01-14# summary: multiprocessing.Lock 测试import osfrom multiprocessing import Pool, Lockfrom functools import partialfrom loguru import loggerdef ProcOne(lock, ch): with lock: logger.info(f'{os.getpid()}-{id(lock)}-{ch*20}') return chdef Main(): line = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' lock = Lock() func = partial(ProcOne, lock) with Pool(4) as p: results = p.map(func=func, iterable=line) print(repr(results))if __name__ == "__main__": Main()运行后果(报错) ...

January 14, 2021 · 2 min · jiezi

关于lock:排队打饭公平锁和非公平锁面试

简介有个小伙伴最近征询我,前段时间他被面试官问了synchronized是偏心锁还是非偏心锁?过后就蒙圈了,最初面试后果可想而知,明天咱们就用一个艰深的案例加上代码来阐明偏心锁和非偏心锁。其实偏心锁这个概念是JUC工具包才有的,比方ReentrantLock才有偏心锁的概念,这篇文章咱们联合生存中的实例用2段代码阐明ReentrantLock偏心锁和非偏心锁,以及证实synchronized是非偏心锁的。心愿对小伙伴有帮忙。 偏心锁、非偏心锁概念偏心锁:举一个简略例子,有五个同学每天必须排队去打饭,为了简略起见,咱们给这五名同学每人定义一个编号,别离为编号001到编号005,这五名同学按先来后到的排队,打饭,先来的同学能先打到饭。每个同学都是一个线程,在这个过程中起初的同学是不容许插队的,这就是偏心锁。非偏心锁:起初到同学不肯定后打到饭,在打饭的过程中,是容许插队的,这种线程插入的行为人们认为是不偏心的。举个例子,比方编号为001,002,003,004的同学先到先排队了,005最初来排队本应该排在004前面的,然而005看001正好打完饭来到,他就去插队了,也就是打饭的程序由001->002->003->004->005变为001->005->002->003->004。其实你当初应该了解了,偏心锁就是失常排队,非偏心就是插队。当然你可能会有疑难?是不是005插到001的前面肯定会胜利,答案是不肯定,这要看机会的,咱们方才说了“005看001正好打完饭来到”,上面应该是002了,可能打饭阿姨还没问002筹备吃什么,就看005曾经排到后面去了,那005就插队胜利了,这就是机会。上面咱们用程序代码来加深了解。synchronized非偏心锁/** * @author :jiaolian * @date :Created in 2020-12-31 16:01 * @description:食堂打饭:synchronized不偏心 * @modified By: * 公众号:叫练 */public class SyncUnFairLockTest { //食堂 private static class DiningRoom { //获取食物 public void getFood() { System.out.println(Thread.currentThread().getName()+":排队中"); synchronized (this) { System.out.println(Thread.currentThread().getName()+":@@@@@@打饭中@@@@@@@"); } } } public static void main(String[] args) { DiningRoom diningRoom = new DiningRoom(); //让5个同学去打饭 for (int i=0; i<5; i++) { new Thread(()->{ diningRoom.getFood(); },"同学编号:00"+(i+1)).start(); } }}如上代码:咱们定义一个外部类DiningRoom示意食堂,getFood办法外面用synchronized锁润饰this指向DiningRoom的实例对象(22行中的diningRoom对象),主类中让编号001至005五个同学同时去打饭,用于测试先排队的同学是否能先打到饭?运行程序失去其中一种执行后果如下图所示,002->004->001->003->005同学先去排队,但打饭的程序是002->003->001->004->005,阐明这里003和001两个同学插队了,插到004后面了,咱们详细分析执行过程,002先抢到锁打饭了,开释了锁,原本应该是接下来是004抢到锁去打饭(因为004是比003先来排队),但003抢到锁,打饭了,开释了锁,这是第一次插队。当初还是来004抢锁,然而没抢到又被001抢到了,开释锁后才被004抢到,这是第二次插队,前面别离再是004->005抢到锁,开释锁,程序执行结束。因为003和001插队,咱们用代码证实了synchronized是非偏心锁。紧接着咱们来看下ReentrantLock偏心锁和非偏心锁。 ...

January 3, 2021 · 1 min · jiezi

关于lock:一种阅读姿势品读Lock和Synchronized锁

1.Synchronized锁底层是monitor监视器,每一个对象再创立的时候都会常见一个monitor监视器,在应用synchronized代码块的时候,会在代码块的前后产生一个monitorEnter和monitorexit指令,来标识这是一个同步代码块。 1.1 执行流程线程遇到同步代码块,给这个对象monitor对象加1,当线程退出以后代码块当前,给这个对象的monitor对象减一,如果monitor指令的值为0则以后线程开释锁。 1.2 反编译源码同步代码块反编译 public void test01(){ synchronized (this){ int num = 1 ; } } 两次monitorexit的作用是防止同步代码块无奈跳出,因而存在两种,失常退出和异样退出 同步办法反编译 public synchronized void test01(){ int num = 1 ; } 能够发现其没有在同步办法前后增加monitor指令,然而在其底层实际上也是通过monitor指令实现的,只不过相较于同步代码块来说,他是隐式的。 1.3 锁降级在JDK1.5的时候对于synchronzied做了一系列优化操作,减少了诸如:偏差锁,轻量级锁,自旋锁,锁粗化,重量级锁的概念。 1.3.1 偏差锁在一个线程在执行获取锁的时候,以后线程会在monitor对象中存储指向该线程的ID。当线程再次进入的时候,不须要通过CAS的办法再来进行加锁或者解锁,而是检测偏差锁的ID是不是以后要进行的线程,如果是,间接进入。 偏差锁,实用于一个线程执行工作的状况 在JDK1.6中,默认是开启的。能够通过-XX:-UseBiasedLocking=false参数敞开偏差锁 1.3.2 轻量级锁轻量级锁是指锁为偏差锁的时候,该锁被其余线程尝试获取,此时偏差锁降级为轻量级锁,其余线程会通过自旋的形式尝试获取锁,线程不会阻塞,从而提供性能 降级为轻量级锁的状况有两种: 敞开偏差锁有多个线程竞争偏差锁的时候具体实现: 线程进行代码块当前,如果同步对象锁状态为无锁的状态,虚拟机将首先在以后线程的栈帧中创立一个锁记录的空间。这个空间内存储了以后获取锁的对象。 应用状况: 两个线程的相互拜访 1.3.3 重量级锁在有超过2个线程拜访同一把锁的时候,锁主动降级为重量级锁,也就是传统的synchronized,此时其余未获取锁的线程会陷入期待状态,不可被中断。 因为依赖于monitor指令,所以其耗费系统资源比拟大 下面的三个阶段就是锁降级的过程 1.3.4 锁粗化当在一个循环中,咱们屡次应用对同一个代码进行加锁,这个时候,JVM会主动实现锁粗化,即在循环外进行增加同步代码块。 代码案例: 锁粗化之前: for (int i = 0; i < 10; i++) { synchronized (LockBigDemo.class){ System.out.println(); } }锁粗化之后: ...

November 12, 2020 · 3 min · jiezi

关于lock:java-手写并发框架二异步转同步框架封装锁策略

序言上一节咱们学习了异步查问转同步的 7 种实现形式,明天咱们就来学习一下,如何对其进行封装,使其成为一个更加便于应用的工具。 思维导图如下: 拓展浏览java 手写并发框架(1)异步查问转同步的 7 种实现形式 异步转同步的便利性实现形式循环期待wait & notify应用条件锁应用 CountDownLatch应用 CyclicBarrierFutureSpring EventListener上一节咱们曾经对下面的 7 种实现形式进行了具体的介绍,没有看过的同学能够去简略回顾一下。 然而这样集体感觉还是不够不便,懈怠是提高的阶梯。 更进一步简化咱们心愿达到上面的成果: @Syncpublic String queryId() { System.out.println("开始查问"); return id;}@SyncCallback(value = "queryId")public void queryIdCallback() { System.out.println("回调函数执行"); id = "123";}通过注解间接须要同步的办法,和回调的办法,代码中间接调用即可。 咱们首先实现基于字节码加强的版本,后续将实现整合 spring, springboot 的版本。 锁的代码实现锁的定义咱们将原来的实现形象为加锁和解锁,为了便于拓展,接口定义如下: package com.github.houbb.sync.api.api;/** * @author binbin.hou * @since 0.0.1 */public interface ISyncLock { /** * 期待策略 * @param context 上下文 * @since 0.0.1 */ void lock(final ISyncLockContext context); /** * 解锁策略 * @param context 上下文 * @since 0.0.1 */ void unlock(final ISyncUnlockContext context);}其中上下文加锁和解锁做了辨别,不过临时内容是一样的。 ...

October 12, 2020 · 4 min · jiezi

关于lock:面试为了进阿里需要深入理解ReentrantLock原理

该系列文章收录在公众号【Ccww技术博客】,原创技术文章早于博客推出前言在面试,很多工夫面试官都会问到锁的问题,ReentrantLock也是常问一个点,但具体会问什么呢?在网上收集到一些问题: 重入锁是什么?偏心锁和非偏心锁是什么?有什么区别?ReentrantLock::lock偏心锁模式事实 ReentrantLock如何实现偏心锁?ReentrantLock如何实现可重入?ReentrantLock偏心锁模式与非偏心锁获取锁的区别?ReentrantLock::unlock()开释锁,如何唤醒期待队列中的线程?ReentrantLock除了可重入还有哪些个性?ReentrantLock与Synchrionized的区别ReentrantLock应用场景那么重入锁是什么?有什么用呢 ReentrantLock是什么?ReentrantLock是个典型的独占模式AQS,同步状态为0时示意闲暇。当有线程获取到闲暇的同步状态时,它会将同步状态加1,将同步状态改为非闲暇,于是其余线程挂起期待。在批改同步状态的同时,并记录下本人的线程,作为后续重入的根据,即一个线程持有某个对象的锁时,再次去获取这个对象的锁是能够胜利的。如果是不可重入的锁的话,就会造成死锁。 ReentrantLock会波及到偏心锁和非偏心锁,实现关键在于成员变量sync的实现不同,这是锁实现互斥同步的外围。 //偏心锁和非偏心锁的变量 private final Sync sync; //父类 abstract static class Sync extends AbstractQueuedSynchronizer {} //偏心锁子类 static final class FairSync extends Sync {} //非偏心锁子类 static final class NonfairSync extends Sync {}那偏心锁和非偏心锁是什么?有什么区别? 那偏心锁和非偏心锁是什么?有什么区别?偏心锁是指当锁可用时,在锁上等待时间最长的线程将取得锁的使用权,即先进先出。而非偏心锁则随机调配这种使用权,是一种抢占机制,是随机取得锁,并不是先来的肯定能先失去锁。 ReentrantLock提供了一个构造方法,能够实现偏心锁或非偏心锁: public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }尽管偏心锁在公平性得以保障,但因为偏心的获取锁没有思考到操作系统对线程的调度因素以及其余因素,会影响性能。 尽管非偏心模式效率比拟高,然而非偏心模式在申请获取锁的线程足够多,那么可能会造成某些线程长时间得不到锁,这就是非偏心锁的“饥饿”问题。 但大部分状况下咱们应用非偏心锁,因为其性能比偏心锁好很多。然而偏心锁可能防止线程饥饿,某些状况下也很有用。 接下来看看ReentrantLock偏心锁的实现: ReentrantLock::lock偏心锁模式实现 首先须要在构建函数中传入true创立好偏心锁 ReentrantLock reentrantLock = new ReentrantLock(true);调用lock()进行上锁,间接acquire(1)上锁 public void lock() { // 调用的sync的子类FairSync的lock()办法:ReentrantLock.FairSync.lock() sync.lock();}final void lock() { // 调用AQS的acquire()办法获取锁,传的值为1 acquire(1);}间接尝试获取锁, ...

September 8, 2020 · 3 min · jiezi