if null 锁 java_史上最全 Java 中各种锁的介绍
什么是锁
在计算机科学中,锁(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制。锁旨在强制实施互斥排他、并发控制策略。
锁通常需要硬件支持才能有效实施。这种支持通常采取一个或多个原子指令的形式,如"test-and-set", "fetch-and-add" or "compare-and-swap"”。这些指令允许单个进程测试锁是否空闲,如果空闲,则通过单个原子操作获取锁。
公平锁
定义:就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。
优点:所有的线程都能得到资源,不会饿死在队列中。
缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
公平锁获取锁例子
1 /**
2 * true 表示 ReentrantLock 的公平锁
3 */
4 private ReentrantLock lock = new ReentrantLock(true);
5
6 public void testFail(){
7 try {
8 lock.lock();
9 System.out.println(Thread.currentThread().getName() +"获得了锁");
10 }finally {
11 lock.unlock();
12 }
13 }
14 public static void main(String[] args){
15 FairLockTest fairLock = new FairLockTest();
16 Runnable runnable = () -> {
17 System.out.println(Thread.currentThread().getName()+"启动");
18 fairLock.testFail();
19 };
20 Thread[] threadArray = new Thread[10];
21 for (int i=0; i<10; i++) {
22 threadArray[i] = new Thread(runnable);
23 }
24 for (int i=0; i<10; i++) {
25 threadArray[i].start();
26 }
27 }
运行结果
1Thread-1启动
2Thread-1获得了锁
3Thread-3启动
4Thread-3获得了锁
5Thread-5启动
6Thread-5获得了锁
7Thread-2启动
8Thread-2获得了锁
9Thread-4启动
10Thread-4获得了锁
11Thread-6启动
12Thread-6获得了锁
13Thread-10启动
14Thread-8启动
15Thread-10获得了锁
16Thread-9启动
17Thread-7启动
18Thread-8获得了锁
19Thread-9获得了锁
20Thread-7获得了锁
看到结果里面获得锁的顺序和线程启动顺序是一致的,这就是公平锁。
非公平锁
定义:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。
优点:非公平锁性能高于公平锁性能,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。
缺点:可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。
非公平锁列子:只需要将上面公平锁的代码改为new ReentrantLock(false);
运行结果
1Thread-1启动
2Thread-0启动
3Thread-2启动
4Thread-3启动
5Thread-4启动
6Thread-8启动
7Thread-7启动
8Thread-6启动
9Thread-1获得了锁
10Thread-0获得了锁
11Thread-5启动
12Thread-5获得了锁
13Thread-2获得了锁
14Thread-3获得了锁
15Thread-4获得了锁
16Thread-8获得了锁
17Thread-7获得了锁
18Thread-6获得了锁
19Thread-9启动
20Thread-9获得了锁
线程启动顺序是1、0、2、3、 4、 8 、7 、6 、5 、9,获得锁的顺序却是1 、0 、5 、2 、3 、4 、8 、7 、6 、9,这就是非公平锁,它不保证先排队尝试去获取锁的线程一定能先拿到锁。
重入锁
定义:- 可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁
下面是一个synchronized重入锁的列子:
1public class ReentrantLockTest{
2
3
4 public static void main(String[] args){
5 for (int i = 0; i
6 new Thread(() -> A()).start();
7 }
8 }
9 public static synchronized void A(){
10 System.out.println(Thread.currentThread().getName());
11 B();
12 }
13 public static synchronized void B(){
14 System.out.println(Thread.currentThread().getName());
15 }
16}
输出:
1Thread-1
2Thread-1
3Thread-0
4Thread-0
A方法和B方法同时输出了线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的。
读写锁
百度百科定义的读写锁是:
读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。
与传统锁不同的是读写锁的规则是可以共享读,但只能一个写,总结起来为:读读不互斥,读写互斥,写写互斥,而一般的独占锁是:读读互斥,读写互斥,写写互斥,而场景中往往读远远大于写,读写锁就是为了这种优化而创建出来的一种机制。注意是读远远大于写,一般情况下独占锁的效率低来源于高并发下对临界区的激烈竞争导致线程上下文切换。因此当并发不是很高的情况下,读写锁由于需要额外维护读锁的状态,可能还不如独占锁的效率高。因此需要根据实际情况选择使用。
Java里面ReentrantReadWriteLock读写锁特性
公平选择性: 支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
重进入: 读锁和写锁都支持线程重进入。
锁降级: 锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。
乐观锁、悲观锁
乐观锁:乐观锁总是认为不存在并发问题,每次去取数据的时候,总认为不会有其他线程对数据进行修改,因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用“数据版本机制”或“CAS操作”来实现。
悲观锁: 悲观锁认为对于同一个数据的并发操作,一定会发生修改的,哪怕没有修改,也会认为修改。因此对于同一份数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁并发操作一定会出问题。典型的数据库的查询 for update。
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。
如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。期间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对jdk1.7 及以前的ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
偏向锁、轻量级锁、重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁:是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁:是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
独享锁、共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。对于Synchronized而言,当然是独享锁。
参考文章
https://blog.csdn.net/qiuwenjie123/article/details/79950532
https://segmentfault.com/q/1010000009659039
https://blog.csdn.net/qq_43519310/article/details/100107346
https://blog.csdn.net/u010648018/article/details/79750608
https://www.cnblogs.com/hustzzl/p/9343797.html
http://ifeve.com/locks/
http://ifeve.com/locks/
if null 锁 java_史上最全 Java 中各种锁的介绍相关推荐
- 2019史上最全java面试题题库大全800题含答案
2019史上最全java面试题题库大全800题含答案 1. meta标签的作用是什么 2. ReenTrantLock可重入锁(和synchronized的区别)总结 3. Spring中的自动装配有 ...
- 2019史上最全java面试题题库大全800题含答案(面试宝典)
2019史上最全java面试题题库大全800题含答案(面试宝典) 1. meta标签的作用是什么 2. ReenTrantLock可重入锁(和synchronized的区别)总结 3. Spring中 ...
- 2022史上最全java面试题题库大全800题含答案
2022史上最全java面试题题库大全800题含答案 1. meta标签的作用是什么 2. ReenTrantLock可重入锁(和synchronized的区别)总结 3. Spring中的自动装配有 ...
- 2023史上最全Java面试题【完整版】跳槽必备,看完轻松收撕面试官
✨作者简介:杨 戬,博客专家.github开源作者 ✨多年工作总结:Java学习路线总结,小白逆袭Java技术总监 ✨技术交流:定期更新Java硬核干货,不定期送书活动.助你实现技术飞跃 ✨关注公众号 ...
- 史上最全 Java 多线程面试题及答案
这篇文章主要是对多线程的问题进行总结的,因此罗列了40个多线程的问题. 这些多线程的问题,有些来源于各大网站.有些来源于自己的思考.可能有些问题网上有.可能有些问题对应的答案也有.也可能有些各位网友也 ...
- 史上最全Java多线程面试题
转载自 史上最全Java多线程面试题及答案 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域.所以,学好多线程并发编程对Java程序员来来说极其重要的. 下面小编整理了60道最常见的 ...
- 史上最全java架构师技能图谱(下)
"java架构史上最全技能图谱分为上下两篇,这是java架构史上最全图谱下篇,包含: 大数据以及性能.设计模式.UML.中间件.分布式集群.负载均衡.通讯协议.架构设计等技术图谱等章节. 如 ...
- 史上最全java架构师技能图谱(上)
java架构师最全技能图谱上篇,包含:数结构算法.java进阶.web开发.框架与工具四大技能图谱. 下篇将包含大数据以及性能.设计模式.UML.中间件.分布式集群.负载均衡.通讯协议.架构设计等技术 ...
- 干货!!史上最全Java学习视频
史上最全Java学习视频下载地址分享 一.java基础 1.毕向东基础,这个最有名了,毕老师还是毕姥爷? 链接:https://pan.baidu.com/s/17bL6ZzegNsUNkM4aBQB ...
最新文章
- 实用网站和在线工具推荐
- UA MATH563 概率论的数学基础 鞅论初步7 停时与Upcrossing不等式
- 开发加速使用maven国内源,感谢阿里技术团队,良心团队!
- UDP模式与TCP模式的区别
- [新功能]Blog首页仅列出标题
- 能干什么_闲鱼流量系统能干什么?真的有用吗?
- MapReduce 支持的部分数据挖掘算法
- 活动目录回收站之终极应用---Windows2008 R2 新功能系列之十一
- 《东周列国志》第五回 宠虢公周郑交质 助卫逆鲁宋兴兵
- 面对未来,与其期待,不如更早开始「自问与探索」| TiDB DevCon 2020 即将开启
- php 786.00,美國佛羅裡達州區號786
- 关系图谱服务的技术方案设计
- Excel常用技巧(一)
- linux cpan 参数配置,Linux下使用CPAN进行Perl模块的安装
- turtle画彩虹蟒蛇
- 程序员的app软件开发经验
- html页面中汉字上面显示拼音
- 【论文阅读】Long-term Temporal Convolutions for Action Recognition
- python编写程序、从键盘输入一个年份_通过计算当年 - 出生年份使用python创建年龄计算器...
- java 屏幕识别_Java课程设计:捕获图片以及识别图中的文字
热门文章
- EMC首席数据治理官:“受托人”是数据湖问责的关键
- 删除a3.txt文件中含dong的行
- nagios 邮件告警
- SQL Server中的执行引擎入门
- Wrong permissions on configuration file, should not be world writable!
- Jquery DataTable控制显示列,导出EXCEL
- Android AdapterView 源码分析以及其相关回收机制的分析
- css实现左侧宽度自适应,右侧固定宽度
- redux常见问题答疑
- 线程及同步的性能 – 线程池/ ThreadPoolExecutors/ ForkJoinPool