[基础课]--[锁]--锁分类​mp.weixin.qq.com

前言

Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8)、使用场景进行举例,为读者介绍主流锁的知识点,以及不同的锁的适用场景。

锁分类

Java中出现的一些锁的基本概念如图所示:

乐观锁 和 悲观锁

乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度.在Java和数据库中都有此概念对应的实际应用.

悲观锁:对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改.Java中,synchronized关键字和Lock的实现类都是悲观锁.

乐观锁:认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据.如果这个数据没有被更新,当前线程将自己修改的数据成功写入.如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试).

乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的.

根据从上面的概念描述我们可以发现:

悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。

乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。

光说概念有些抽象,我们来看下乐观锁和悲观锁的调用方式示例:

通过调用方式示例,我们可以发现悲观锁基本都是在显式的锁定之后再操作同步资源,而乐观锁则直接去操作同步资源。

自旋锁 VS 适应性自旋锁

在介绍自旋锁前,我们需要介绍一些前提知识来帮助大家明白自旋锁的概念。我们都知道阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。这时候为了优化这种消耗,就引入了自旋锁.当有线程持有锁的时候,另一个线程不会直接挂起,而是等一下,等待上一个线程执行结束,这样就可以避免线程切换的开销,这就是自旋锁.

从定义中我们也可以看出,自旋锁是有缺陷的,当等待的线程长时间不释放锁的时候,自选等待的效果就很差,甚至会比线程切换更消耗时间.所以我们需要添加一个阈值限制自旋的次数(默认为10,-XX:PreBlockSpin更改),当数量到达后就不再自旋,挂起线程.

自旋锁的实现原理同样也是CAS,AtomicInteger中调用unsafe进行自增操作的源码中的do-while循环就是一个自旋操作,如果修改数值失败则通过循环来执行自旋,直至修改成功。

自旋锁在JDK1.4.2中引入,使用-XX:+UseSpinning来开启。JDK 6中变为默认开启,并且引入了自适应的自旋锁(适应性自旋锁)。

自适应意味着自旋的时间(次数)不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。

在自旋锁中 另有三种常见的锁形式:TicketLock、CLHlock和MCSlock,本文中仅做名词介绍,不做深入讲解,感兴趣的同学可以自行查阅相关资料。

公平锁 和 非公平锁

公平锁:多个线程按照申请锁的顺序获取锁.

非公平锁:非顺序获取锁,多个线程获取锁的顺序并不是按照申请锁的顺序、有可能后申请的先获取到锁、或造成优先级反转或者锁饥饿现象.

如图所示,打水的人会排队从管理员处获取打水资格后打水,和队列一样先进先出顺序执行.谁先来谁先拿锁,公平锁.

如图所示,打水的人虽然会排队从管理员处获取打水资格后打水,但可以插队获取锁.非公平锁.

可重入锁 和 非可重入锁

可重入锁:又称递归锁,可以多层获取锁,在外层申请到锁后,内层仍可以使用锁并不会发生死锁(前提是同一个对象或者Class)这种锁被称为可重入锁.如 ReentrantLock、synchronized等

如图就是可冲入锁synchronized的使用

非可重入锁:和可重入锁相反,不可递归调用,递归调用会发生死锁.

独占锁 和 共享锁

独享锁和共享锁是一种概念,类似悲观锁和乐观锁. 独占锁:也叫排他锁,该锁每一次只能有一个线程持有。比如当数据A被线程T加锁后,其他线程不可用再对数据A加任何其他的锁.只有获取该排它锁的线程可以对数据进行查看和修改.JDK中的synchronized和Lock实现类都是独占锁.

共享锁:该锁可以被多个线程共有.如果线程T对数据A加上共享锁后,则其他线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据.

独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享.

分段锁

分段锁 其实是一种锁的设计思想,他并不是一种锁.典型的实现就是ConcurrentHashMap,其并发操作就是使用分段锁来设计实现的以达到高效的并发操作.

结语

本文Java中常用的锁以及常见的锁的概念进行了基本介绍,接下来的章节小哈会对照JDK的源码实现来详解介绍各个类型锁的实现和原理.

其实Java本身已经对锁本身进行了良好的封装,降低了研发同学在平时工作中的使用难度。但是研发同学也需要熟悉锁的底层原理,不同场景下选择最适合的锁。而且源码中的思路都是非常好的思路,也是值得大家去学习和借鉴的。

java分类锁_【基本功】java锁分类详解相关推荐

  1. java enum 父类_枚举基类Enum详解

    本文主要是对枚举类型的基类Enum类做一个介绍: 首先,Enum类位于java.lang包下,根据类的介绍可以发现,Enum类是Java中所有枚举类的父类,将枚举作为一个set或者Map的keys来使 ...

  2. java udp 协议_网络协议 - UDP 协议详解

    ¶ 网络协议 - UDP 协议详解 基于TCP和UDP的协议非常广泛,所以也有必要对UDP协议进行详解.@pdai ¶ UDP概述 UDP(User Datagram Protocol)即用户数据报协 ...

  3. java cookie路径_路径问题以及cookie详解

    1.路径问题: 注意 .代表执行程序的文件夹路径,在tomcat中也就是bin目录,所以要用this.getServletContext().getRealPath("/WEB-INF/cl ...

  4. java response 对象_常用response对象的详解

    每一个程序语言或开发工具都有一定的函数与用户进行沟通,Asp同样如此.在Asp中负责将信息传递给用户的对象就是Response对象.Response对象用于动态响应客户端请求(Request),并将动 ...

  5. java path类_基于java Files类和Paths类的用法(详解)

    Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream; import java.nio.file.FileSystem ...

  6. java中sleep()、wait()相同与不同详解

    java中sleep().wait()相同与不同详解 相同 java中Thread#sleep和Object#wait方法都是暂停当前线程,当前线程让出CPU占用.并不存在调用sleep后还占用CPU ...

  7. Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载)

    Java开源生鲜电商平台-Java分布式以及负载均衡架构与设计详解(源码可下载) 说明:主要是针对一些中大型的项目需要进行分布式以及负载均衡的架构提一些思路与建议. 面对大量用户访问.高并发请求,海量 ...

  8. java工程在windows环境用bat启动详解

    原 java工程在windows环境用bat启动详解 2016年08月24日 13:19:52 qq_31197461 阅读数:3038 <span class="tags-box a ...

  9. java加密算法入门(三)-非对称加密详解

    1.简单介绍 这几天一直在看非对称的加密,相比之前的两篇内容,这次看了两倍多的时间还云里雾里的,所以这篇文章相对之前的两篇,概念性的东西多了些,另外是代码的每一步我都做了介绍,方便自己以后翻阅,也方便 ...

  10. java文件流 m.jb51.net_FasfDFS整合Java实现文件上传下载功能实例详解

    今天使用Java代码实现文件的上传和下载.对此作者提供了Java API支持,下载fastdfs-client-java将源码添加到项目中.或者在Maven项目pom.xml文件中添加依赖 org.c ...

最新文章

  1. svn官方备份hot-backup.py强烈推荐
  2. 2019年11个值得研究的Javascript机器学习库
  3. MapRdeuceYarn的工作机制(YarnChild是什么)
  4. WPF纯手工两步打造图片切割工具(一)
  5. PCL点云曲面重采样三种方法:上采样,下采样,均匀采样
  6. YbtOJ-变量观测【鸽笼原理】
  7. 年近而立,Java何去何从?
  8. CodeIgniter典型的表单提交验证代码
  9. Minimum Inversion Number 线段树
  10. CSS + DIV 让页脚始终保持在页面底部
  11. 消息长度_nsq消息队列源码分析
  12. jenkins设置构建触发器
  13. BI系统AWS云迁移方案设计(通用)
  14. 微信圣诞帽:OpenCV 库Linux下c++实现
  15. 摄像头视频推流python_python中用FFmpeg向rtmp服务器推流,实现摄像头直播
  16. (13.1.2)PMBOK之二:五大过程组及其涉及的输入、输出、工具技术
  17. AES - Openssl AES 函数说明
  18. Linux 关闭防火墙方法
  19. 数据结构题及c语言版严第七章答案,数据结构第七章习题答案.doc
  20. 双非长沙理工排名飙升176反超吉大,湖大连续2年超哈工大 | US News 2023

热门文章

  1. percona 软件介绍
  2. python判断三位数水仙花数_python 判断一个三位数是不是水仙花数
  3. 【区块链杂谈】区块链的前世今生(前世)
  4. 浏览器缓存是什么?它的机制又是什么?
  5. 官宣!华为断臂为了自救
  6. git本地仓库与远程仓库
  7. Java学习------第一周知识点总结
  8. 【Android SurfaceView】视频播放器 简单例子
  9. linux 通配符 正则表达式 区别,linux 正则表达式和通配符
  10. GreenDao的使用以及断点续传