关于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

Java并发ReentrantLock

1.简介可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似,但是功能上比 synchronized 更强大,除可重入之外,ReentrantLock还具有4个特性:等待可中断、可实现公平锁、可设置超时、以及锁可以绑定多个条件。在synchronized不能满足的场景下,如公平锁、允许中断、需要设置超时、需要多个条件变量的情况下,需要考虑使用ReentrantLock。 2.用法ReenTrantLock继承了Lock接口,Lock接口声明有如下方法: 2.1 可重入锁可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。 void m1() { lock.lock();try { // 调用 m2,因为可重入,所以并不会被阻塞 m2();} finally { lock.unlock()}} void m2() { lock.lock();try { // do something} finally { lock.unlock()}} 注:ReentrantLock的方法需要置于try-finally块中,需要在finally中释放锁,防止因方法异常锁无法释放。 2.2 可中断锁在等待获取锁过程中可中断。注意是在等待锁过程中才可以中断,如果已经获取了锁,中断就无效。调用锁的lockInterruptibly方法即可实现可中断锁,当通过这个方法去获取锁时,如果其他线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就是说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。示例如下: public class ReentrantLockTest { private static int account = 0;private static ReentrantLock lock = new ReentrantLock();public static void main (String [] args) { Thread t1 = new Thread(()->{ try { lock.lockInterruptibly(); System.out.println("线程t1输出:"+account++); } catch (InterruptedException e) { System.out.println("线程t1被中断了"); }finally { lock.unlock(); } },"t1"); Thread t2 = new Thread(()->{ try { lock.lockInterruptibly(); System.out.println("线程t2输出:"+account++); // 调用interrupt方法中断线程t1 t1.interrupt(); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程t2被中断了"); }finally { lock.unlock(); } },"t2"); t2.start(); t1.start(); }} ...

September 19, 2019 · 3 min · jiezi

Java并发8Lock和Condition上-隐藏在并发包中的管程

Java SDK 并发包内容很丰富。但是最核心的还是其对管程的实现。因为理论上利用管程,你几乎可以实现并发包里所有的工具类。在前面我们提到过在并发编程领域,有两大核心问题:一个是互斥:即同一时刻只允许一个线程访问共享资源;另一个是 同步:即线程之间如何通信、协作。 这两大问题,管程都是能够解决的。Java SDK 并发包通过 Lock 和 Condition 两个接口来实现管程,其中 Lock 用于解决互斥问题,Condition 用于解决同步问题。 今天我们重点介绍 Lock 的使用,在介绍 Lock 的使用之前,有个问题需要你首先思考一下:Java 语言本身提供的 synchronized 也是管程的一种实现,既然 Java 从语言层面已经实现了管程了,那为什么还要在 SDK 里提供另外一种实现呢?很显然它们之间是有巨大区别的。那区别在哪里呢? 再造管程的理由让我们回顾下在之前的死锁问题中。提出一个破坏不可抢占条件的方案。但是这个方案 synchronized 没有办法解决。原因是 synchronized 申请资源的时候,如果申请不到,线程直接进入阻塞状态了,而线程进入阻塞状态,啥都干不了,也释放不了线程已经占有的资源。但我们希望的是: 对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。如果我们重新设计一把互斥锁去解决这个问题,那该怎么设计呢?我觉得有三种方案。 1. 能够响应中断synchronized 的问题是,持有锁 A 后,如果尝试获取锁 B 失败,那么线程就进入阻塞状态,一旦发生死锁,就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号,也就是说当我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。 2. 能够支持超时如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。 3. 非阻塞地获取锁如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。 这三种方案可以全面弥补 synchronized 的问题。这三个方案就是“重复造轮子”的主要原因,体现在 API 上,就是 Lock 接口的三个方法。详情如下: // 支持中断的 APIvoid lockInterruptibly() throws InterruptedException;// 支持超时的 APIboolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 支持非阻塞获取锁的 APIboolean tryLock();如何保证可见性Java SDK 里面 Lock 的使用,有一个经典的范例,就是try{}finally{}。需要重点关注的是在 finally 里面释放锁。这个范例无需多解释。但是有一点需要解释一下,那就是可见性是怎么保证的。你已经知道 Java 里多线程的可见性是通过 Happens-Before 规则保证的,而 synchronized 之所以能够保证可见性,也是因为有一条 synchronized 相关的规则:synchronized 的解锁 Happens-Before 于后续对这个锁的加锁。那 Java SDK 里面 Lock 靠什么保证可见性呢?例如在下面的代码中,线程 T1 对 value 进行了 +=1 操作,那后续的线程 T2 能够看到 value 的正确结果吗? ...

May 12, 2019 · 2 min · jiezi

Golang的分布式锁组件,支持Reids,Pgsql或自定义驱动

通用的Golang分布式锁组件,项目地址:https://github.com/go-locks/d…,更多使用案例详见 examplesDriver列表若有意向贡献未完成的驱动代码,请通过 ISSUES 或 邮箱 249008728@qq.com 联系我Driver代码完成度测试完成度依赖包使用说明redis100%100%letsfire/redigo详见 README.mdpgsql100%100%lib/pq详见 README.mdetcd未完成未测试etcd/client详见 README.mdetcdv3未完成未测试etcd/clientv3详见 README.md方法说明配置项 mutex.OptFunc 以及返回值锁的使用详见 mutex/README.mdNewMutex(name string, optFuncs …mutex.OptFunc) (*mutex.Mutex, error)创建互斥锁,若 name 已用于创建读写锁则返回 error,本方法单例模式NewRWMutex(name string, optFuncs …mutex.OptFunc) (*mutex.RWMutex, error)创建读写锁,若 name 已用于创建互斥锁则返回 error,本方法单例模式注意事项不可重入(如果您有强烈的需求场景,请通过 ISSUES 提供反馈)非公平锁(Golang的本地锁 sync.Locker 视乎也不是公平锁,若您有需求或建议,请通过 ISSUES 提供反馈)有互斥锁 mutex 和 读写锁 rwmutex 两种类型,具体支持程度详见各个 Driver 对应的 README.md本人项目经验少,实现过程难保欠缺一些场景的考虑,望大家见谅,若能通过 ISSUES 提供反馈则感激涕零虽有完整的单元测试,但暂未经过实际项目考验,故慎用于生产环境,如有问题请通过 ISSUES 来共同完善项目结构主线调用层级为 distlock.go -> mutex.go -> driver.godistlock.go 提供了创建锁的工厂类,单例模式(相同名称的锁有且仅有一个,有且仅为一种)mutex.go提供了各类锁的实现,欢迎各位同学贡献其他类型锁,详见 mutex/README.mddriver.go提供驱动接口的定义,欢迎各位同学贡献其他驱动,详见 driver/README.md

January 27, 2019 · 1 min · jiezi

Java Lock示例 - ReentrantLock

引言在多线程环境下,通常我们使用 synchronized 关键字来保证线程安全。大多数情况下,用 synchronized 关键字就足够了,但它也有一些缺点, 所以在 Java Concurrency 包中引入了 Lock API 。从Java 1.5版开始在 java.util.concurrent.locks 包中提供了处理并发的 Concurrency API 的 Lock 锁接口和一些实现类来改进 Object 锁定机制。Java Lock API中的一些重要接口和类Java Lock API中的一些重要接口和类是:锁(Lock):这是Lock API的基本接口。它提供了 synchronized 关键字的所有功能,以及为锁定创建不同条件的其他方法,为线程等待锁定提供超时功能。一些重要的方法是 lock() 获取锁,unlock() 释放锁,tryLock() 等待锁定一段时间,newCondition() 创建条件等。条件(Condition):条件对象类似于对象等待通知( Object wait-notify)模型,具有创建不同等待集的附加功能。Condition 对象始终由 Lock 对象创建。一些重要的方法是 await(),类似于Object.wait() 和 signal(),signalAll(),类似于 Object.notify() 和 Object.notifyAll() 方法。读写锁(ReadWriteLock):它包含一对关联的锁,一个用于只读操作,另一个用于写入。只要没有写入线程,读锁可以由多个读取线程同时保持。写锁是独占的。重入锁(ReentrantLock):这是最广泛使用的 Lock 接口实现类。此类以与 synchronized 关键字类似的方式实现 Lock 接口。除了 Lock 接口实现之外,ReentrantLock 还包含一些实用程序方法来获取持有锁的线程,等待获取锁线程等。synchronized 块synchronized 块本质上是可重入的,即如果一个线程锁定了监视对象,并且另一个同步块需要锁定在同一个监视对象上,则线程可以进入该代码块。我认为这就是类名是ReentrantLock的原因。让我们通过一个简单的例子来理解这个特性。public class Test{public synchronized foo(){ //do something bar(); } public synchronized bar(){ //do some more }}如果一个线程进入 foo(),它就会锁定Test对象,所以当它尝试执行 bar() 方法时,允许该线程执行 bar() 方法,因为它已经在 Test 对象上持有锁,即与 synchronized(this) 效果是一样的。Java Lock 示例 - Java 中的 ReentrantLock现在让我们看一个简单的例子,我们将使用 Java Lock API 替换 synchronized 关键字。假设我们有一个 Resource 类,其中包含一些操作,我们希望它是线程安全的,以及一些不需要线程安全的方法。package com.journaldev.threads.lock;public class Resource { public void doSomething(){ //do some operation, DB read, write etc } public void doLogging(){ //logging, no need for thread safety }}现在假设我们有一个 Runnable 类,我们将使用 Resource 方法。package com.journaldev.threads.lock;public class SynchronizedLockExample implements Runnable{ private Resource resource; public SynchronizedLockExample(Resource r){ this.resource = r; } @Override public void run() { synchronized (resource) { resource.doSomething(); } resource.doLogging(); }}请注意,我使用 synchronized 块来获取 Resource 对象上的锁。我们可以在类中创建一个虚拟对象,并将其用于锁定的目的。现在让我们看看我们如何使用 Java Lock API 并重写上面的程序而不使用 synchronized 关键字。我们将在Java 中使用 ReentrantLock。package com.journaldev.threads.lock;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ConcurrencyLockExample implements Runnable{ private Resource resource; private Lock lock; public ConcurrencyLockExample(Resource r){ this.resource = r; this.lock = new ReentrantLock(); } @Override public void run() { try { if(lock.tryLock(10, TimeUnit.SECONDS)){ resource.doSomething(); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ //release lock lock.unlock(); } resource.doLogging(); }}正如你所看到的,我正在使用 tryLock() 方法来确保我的线程只等待一定的时间,如果它没有获得对象的锁定,它只是记录和退出。另一个要注意的重点是使用 try-finally 块来确保即使 doSomething() 方法调用抛出任何异常也会释放锁定。Java Lock 与 synchronized 比较基于以上细节和程序,我们可以很容易地得出 Java Lock 和同步之间的以下差异。Java Lock API 为锁定提供了更多的可见性和选项,不像在线程可能最终无限期地等待锁定的同步,我们可以使用tryLock() 来确保线程仅等待特定时间。用同步关键字的代码更清晰,更易于维护,而使用 Lock,我们不得不尝试使用 try-finally 块来确保即使在 lock() 和 unlock() 方法调用之间抛出异常也会释放 Lock。同步块或方法只能覆盖一种方法,而我们可以在一种方法中获取锁,并使用 Lock API 在另一种方法中释放它。synchronized 关键字不提供公平性,而我们可以在创建 ReentrantLock 对象时将公平性设置为 true,以便最长等待的线程首先获得锁定。我们可以为 Lock 创建不同的等待条件(Condition),不同的线程可以针对不同的条件来 await() 。这就是 Java Lock 示例,Java 中的 ReentrantLock 以及使用 synchronized 关键字的比较分析。作 者:关于Pankaj如果你走得这么远,那就意味着你喜欢你正在读的东西。为什么不直接在Google Plus,Facebook或Twitter上与我联系。我很想直接听到你对我的文章的想法和意见。最近我开始创建视频教程,所以请在Youtube上查看我的视频。 ...

December 31, 2018 · 2 min · jiezi