Java并发编程锁之独占公平锁与非公平锁比较

公平锁和非公平锁理解:

在上一篇文章中,我们知道了非公平锁。其实Java中还存在着公平锁呢。公平二字怎么理解呢?和我们现实理解是一样的。大家去排队本着先来先得到的原则,在排队中,无论身份贵贱,一律平等对待。这是就是我们现实生活中的公平。大家都喜欢公平的。但是在Java中默认是非公平的,为什么呢?

本文主要内容:公平锁的现实生活理解;公平锁演示;为什么Java中默认是非公平锁(公平锁的非公平锁的比较)

本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《Lock系列》教程的第四篇:《Java并发包下锁学习第五篇:公平锁理解及与非公平锁的比较》。

生活中的例子:

同样还是去ATM机取钱的例子。假设现在有3个人使用ATM取钱。路人甲不会用ATM,自己摸索耗时5min,然后终于学会怎么使用了,但是密码又忘掉了。打电话给家里人咨询耗时1min.当路人甲操作完成之后,后面两个人排队接着依次操作,这种方式是谁先到谁先操作,操作完成之后下一个人才可以操作的,不管贫富贵贱,不管你是取100还是取1W,取1W的人在取100的人后面,就要排着队等着,这种看上去很公平的,无论贵贱,大家依次操作,这种操作模式站在多线程并发角度来看的话,就是公平锁操作。

在路人甲总耗时6min之后,路人乙和路人丁两个人操作耗时3min。也就是三个人总耗时9min.为什么会产生这种情况呢?因为路人甲堵着了。一直占着锁的资源。在路人甲操作的过程中,其他人只能排队等待。如果路人甲不会操作,排在他后面的路人丙插队询问路人甲,自己可以先插队操作ATM,同时教会路人甲。如果甲同意,则丙可以操作完成后,甲可以学着别人操作,有可能路人甲2min也能操作完成。这个时候,三个人总耗时就是5min了;如果甲不同意丙插队操作,那么丙只能回到原来位置上,进行排队等待。这种操作模式站在多线程并发角度来考虑的话,路人甲在模式及和家人通话耗时看着是CPU切换上下文的耗时。路人丙插队后获取ATM资源,这个操作可以看着是非公平的,因为丙的进入时间比路人乙晚,但是丙先操作了。但是从最后三人总耗时来看,路人丙插队,是的效率提高了。这种操作在Java并发中,称之为非公平锁。

需要说明的是,无论是显式锁还是隐式锁默认都是非公平的。因为非公平能够提升系统的吞吐量。

非公平锁的定义:

线程先尝试着获取同步状态操作(丙先尝试着插队),如果获取到,就对共享变量操作(甲同意丙的插队,丙就操作ATM机),如果获取不到,就接着排队。

使用方法二:独占公平演示

需求:控制台打印的结果和线程顺序一致。代码如下:

此时代码和上一次代码唯一区别如上图:在实例化lock的是有个参数,设置了true.

运行结果:

从运行结果中,我们发现控制台打印出的获取锁的顺序和调用锁的时候顺序是一样的, 已经达到我们预期结果了。但是,还是每次只有一个线程操作,等这个线程操作完成释放锁后,其他线程才可以接着获取锁。

公平锁与非公平锁的比较

问题:

为什么并发大师Doug Lea把ReentrantLock设计默认模式是非公平的?

其实要回答这个问题,就需要从公平锁与非公平锁的不同来进行比较了。我们先来看看ATM机操作在公平锁和非公平锁的场景下,如下图:

公平锁:大家都排队,如果一个线程堵着了(路人甲),其他线程只能等待这个。最终,三个线程操作完成,总耗时9min.

非公平锁情况下:多个线程操作的共享资源的时候,发现共享资源还没有被锁定(路人甲还在摸索过程),就尝试插队(路人丙尝试和甲沟通,先插队操作并教会甲),如果插队成功(甲同意了),就操作共享资源(丙先操作ATM机);如果插队失败(甲不同意),接着排队(丙回到队伍中排队)。如果插队成功,最终耗时:5min.

从中我们可以看出公平锁和非公平锁的优缺点了。

优缺点比较:

非公平锁:

优点:效率高;缺点:容易导致线程“饥饿”。当多个线程使用非公平的话,有可能有一个线程一直就获取不到竞争权,导致这个线程会“饥饿而死”。

适用场景:

如果在不考虑TPS(单位时间内成功完成的次数)作为唯一考量指标的场景下,可以使用非公平锁来操作,因为非公平锁能提高系统的吞吐量;

公平锁:

优点:避免了线程的“饥饿”;缺点:性能相对于公平锁会差很多。

​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​    ​        ​    ​                  ​​欢迎来聊

Java并发编程锁之独占公平锁与非公平锁比较的更多相关文章

  1. Java并发编程:用AQS写一把可重入锁

    Java并发编程:自己动手写一把可重入锁详述了如何用synchronized同步的方式来实现一把可重入锁,今天我们来效仿ReentrantLock类用AQS来改写一下这把锁.要想使用AQS为我们服务, ...

  2. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  3. Java并发编程:自己动手写一把可重入锁

    关于线程安全的例子,我前面的文章Java并发编程:线程安全和ThreadLocal里面提到了,简而言之就是多个线程在同时访问或修改公共资源的时候,由于不同线程抢占公共资源而导致的结果不确定性,就是在并 ...

  4. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格依照FIFO的队列.他可以确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  5. java并发编程的艺术——第五章总结(Lock锁与队列同步器)

    Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...

  6. 【漫画】JAVA并发编程 J.U.C Lock包之ReentrantLock互斥锁

    在如何解决原子性问题的最后,我们卖了个关子,互斥锁不仅仅只有synchronized关键字,还可以用什么来实现呢? J.U.C包中还提供了一个叫做Locks的包,我好歹英语过了四级,听名字我就能马上大 ...

  7. 转:【Java并发编程】之一:可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  8. 【Java并发编程】之一:可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  9. java并发编程(一)可重入内置锁

    每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...

  10. java并发编程(8)原子变量和非阻塞的同步机制

    原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用 ...

随机推荐

  1. python基础知识的重新认识

    昨天模拟书本上client和server交互的例子,代码明明是按照书上写的,可是就是出现了错误,像下面这样: # tcpserver from socket import * from time im ...

  2. SpringBoot&Shiro实现权限管理

    SpringBoot&Shiro实现权限管理 引言 相信大家前来看这篇文章的时候,是有SpringBoot和Shiro基础的,所以本文只介绍整合的步骤,如果哪里写的不好,恳请大家能指出错误,谢 ...

  3. 论nw.js的坑~~~感觉我所有的前端能遇到的坑都踩了一遍

    先总结:nw.js 真特么的...难用...文档,我得先百度才能看的稍微明白点文档......!!!!!!我感觉我所有的前端能遇到的坑都踩了一遍,此文针对前后端分离项目,别的先不说 一.不需要在项目里 ...

  4. hadoop HDFS扩容

    1.纵向扩容(添加硬盘) 1.1 添加硬盘 确定完成添加,运行 lsblk 查看硬盘使用情况 1.2 硬盘分区 fdisk /dev/sdb #对新硬盘sdb进行分区 m 帮助 n 添加一个分区 p ...

  5. ARM AVI 视频播放 基于 2440 freambuffer 绘制

    AVI 文件格式 图解 整理: AVI 是一种容器,里面放什么格式的数据都可以 视频编码:格式可以是 H264, MPEG-4, MJPEG ,因为 MJPEG 比较简单,所以仅支持 MJPEG 的 ...

  6. Simulink仿真入门到精通(十三) Simulink创建自定义库

    当用户自定义了一系列模块之后,可以自定义模块库将同类自定义模块显示到Simulink Browser中,作为库模块方便地拖曳到新建模型中. 建立这样的自定义库需要3个条件: 建立library的mdl ...

  7. Button相关设置

    2020-03-11 每日一例第4天 1.添加按钮1-6,并修改相应的text值:  2.窗体Load事件加载代码: private void Form1_Load(object sender, Ev ...

  8. js中字符串 stringObject 的 replace() 方法

    一.定义 replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的字符串. 二.语法 stringObject.replace(regexp/substr,repl ...

  9. 爬虫前奏——初谈Requests库

    什么是Requests Requests是用python语言基于urllib编写的,采用的是Apache2 Licensed开源协议的HTTP库如果你看过上篇文章关于urllib库的使用,你会发现,其 ...

  10. 【题解】NOIP 2015 子串

    淦!这题我做了三个月啊 题目描述 有两个仅包含小写英文字母的字符串 \(A\) 和 \(B\). 现在要从字符串 \(A\) 中取出 \(k\) 个互不重叠的非空子串,然后把这 \(k\) 个子串按照 ...