JAVA并发——公平锁,非公平锁,悲观锁,乐观锁,死锁

个人博客:haichenyi.com。感谢关注

前言

  这几个锁都可以从前面一篇线程同步器AQS里面找到影子,我先把前面一篇的加锁流程图拿过来用一用。

加锁流程图.png

  上面这个流程图是上一篇最开始讲的时候的一张流程图,后面写的时候,后面的流程图都没有画。这一片我们来画一下后面的流程图。

公平锁,非公平锁

  前面一篇讲的时候,我说过了,我们当时做的是一个公平锁。这个公平锁和非公平锁的主要区别就是在这个队列。

  我们前文讲过了,线程1拿到了锁,线程2,3,4就全部放进队列中等待,那么,流程图如下:

等待流程图1.png

  如上图,我们理想状态是:线程1释放锁的时候,队列中的第一个元素,也就是线程2拿到锁,然后,开始执行。

  但是,往往不如意,谁规定的一共就只有4个线程呢?如果,我们正当1释放锁的同时,又有一个线程5进来了,我们要怎么操作呢?流程图如下:

等待流程图2.png

  公平锁和非公平锁的区别就在这里:

  1. 公平锁会把线程5放进队列中,放到线程4的后面,线程2获取到锁,然后执行自己的任务
  2. 非公平锁则是,线程1释放锁之后,状态变成了0,线程5去竞争锁,获取到锁之后,状态state又变成了1,线程2被唤醒之后,正准备去获取锁的时候,一看,状态state是1,又进入等待状态。

  所以,公平锁就是释放锁之后,谁等待得时间长,谁先执行。非公平锁则是,释放锁之后,谁先获取到锁,谁先执行。可能后进的执行,也可能先进的先执行。

  ReentrantLock,初始化的时候传true就是公平锁,传false就是非公平锁,默认是非公平锁。下面就是ReentrantLock的构造方法。

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

悲观锁锁,乐观锁

  悲观锁:操作之前加锁,操作完成之后解锁。我们前文讲的buy方法就是悲观锁,进入方法就加锁,方法执行完就解锁。还有我们常用的synchronized关键字,就是悲观锁的典型代表。

  乐观锁:乐观锁是一种思想,比方说,我们前文提到的CAS机制,就是乐观锁的一种实现。当我们操作一个变量做加减操作的时候,我们多个线程可以同时做这个操作,但是到具体更新这个值的时候,去判断。典型代表就是Atomic原子类。这个原子类的实现也是CAS机制。

  性能问题:多个线程同时执行,悲观锁,就只有一个线程操作,其他线程挂起等待,释放锁之后,再切换回来。乐观锁,所有的线程都一起执行,最后执行冲突检测和数据更新操作。没有挂起等待,上下文的切换,所以,乐观锁的性能肯定比悲观锁好。但是,实际上真的是这样吗?答案是否定的。乐观锁的性能不一定比悲观锁好。

  前面,我们说到乐观锁是在最后更新得时候,去判断。那么怎么判断呢?早期1.5版本之前的CAS操作是有3个参数内存位置(V)、原值(A)、新值(B)。我们在更新的时候,先判断A是否满足,满足就更新成B上一篇文章已经说过了。不满足,那就再循环一边重复判断。极端情况下,要是线程足够的多,并且一直不满足,那是不是一直循环判断(CAS自旋)?那就一直占用CPU。这样性能肯定不好。

  synchronized在JDK1.5之前的确性能很差,但是在1.6的时候就已经做了优化了,从无锁状态,到偏向锁状态,再到轻量级锁状态,最后到重量级锁状态。这几个状态会随着竞争情况逐渐升级(锁不但可以升级还可以降级)。现在synchronized的性能跟ReentrantLock差不多。

  所以,悲观锁的性能不一定比乐观锁差,乐观锁的性能不一定比悲观锁好。根据实际情况去选择悲观锁和乐观锁。那到底怎么选择呢?

  之前在网上看到过这么一组数据,启用多个线程进行计数相加到一亿,首先是synchronized方式

synchronized时间图.png

  当线程数为8时,性能明显提升,但是8到32个线程来说,每个线程的平均时间基本差不多,基本没有提升,到了64个线程的时候,性能又有一点提升。

如果换成CAS实现多线程累加数为一亿,时间又会怎么样呢?

CAS时间图.png

  在线程数相对较少的时候,CAS实现比较快,性能优于synchronized,当线程数多于8后,CAS实现明显开始下降,反而时间消耗高于synchronized;

  总结:synchronized是java提供的又简单方便,性能优化又非常好的功能,建议大家常用;CAS的话,线程数大于一定数量的话,多个线程在循环调用CAS接口,虽然不会让其他线程阻塞,但是这个时候竞争激烈,会导致CPU到达100%,同时比较耗时间,所以性能就不如synchronized了。

死锁

概念

  死锁是指:多个进程在运行过程中因争夺某一种资源,而造成的僵持状态,若无外力作用,他们都将无法向前推进。

举个栗子

小明在看电视,小红在玩手机,小明对小红说:你把手机给我玩,我把点视给你看;小红却说:你把点视给我看,我再把手机给你玩。

分析

  1. 电视,手机都可以看作一种资源。
  2. 小明在看电视,小红在玩手机:表示电视分配给小明了,小明对电视持有锁;手机分配给小红了,小红对手机持有锁
  3. 小明对小红说,你把手机给我玩,我把点视给你看:小明想获取到手机的锁之后,再释放自己电视的锁。
  4. 小红却说,你把点视给我看,我再把手机给你玩:小红想获取电视的锁之后,再释放自己手机的锁

  所以,小明和小红都在等待对方释放锁,自己拿到想要的资源之后,释放自己资源的锁。这里谁都拿不到锁,就无线的等待下去。这就是死锁。

产生条件

  1. 互斥条件:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。(就是这里的电视只能给小明看,手机只能给小红玩)
  2. 占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。(小明等着小红释放手机资源,小红等着小明释放电视资源)
  3. 不剥夺条件:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来(小明在看电视的时候,小红不能说,我要看电视,你给我看。小红在玩手机的时候,小明不能说,我要玩手机,你把手机给我玩。我们要讲文明,不能耍流氓)
  4. 环路等待条件:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源(小明等着小红释放手机资源,小红等着小明释放电视资源)

解决办法

  如果产生死锁只能重启。所以,我们在开发过程中要尽量避免死锁,比方说:著名的银行家算法。只要上面四种中的任意一种不满足,就不可能造成死锁:比方说占有等待,我们可以用共享锁的方式AQS里面每个加锁的方法都有一个try开头的方法。可以看一下acquire和tryAcquire的区别。这就破坏了第二个条件,等待。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容