关于Java中的锁,看这一篇就够了(总结篇)
文章目录
- 锁的概念
- 锁的分类
- 一、什么是悲观锁?
- Java中的悲观锁有哪些
- synchronized
- 基于AQS的实现类
- 二、什么是乐观锁?
- Java中的乐观锁有哪些
- Valotile
- Java内存模型
- CAS(Compare And Set)
- Valotile+CAS
- 总结
锁的概念
java当中的锁、是在多线程环境下为保证共享资源健康、线程安全的一种手段。
线程操作某个共享资源之前,先对资源加一层锁,保证操作期间没有其他线程访问资源,当操作完成后,再释放锁。
锁的分类
锁有哪些?我们经常会听到比如:读写锁、公平锁、非公平锁、乐观锁、悲观锁、自旋锁、可重入锁……等等
看一张图(来源网络):
以上都属于是一些锁的名称、属性。以Java来说,关于锁的大分类,就只有:悲观锁、乐观锁这两种。其余说的各种锁都是基于这两大分类下的细节实现。
一、什么是悲观锁?
基本上我们理解的操作前对资源加锁,操作完后释放锁。说的都是悲观锁。悲观锁认为所有的资源都是不安全的,随时会被其他线程操作、更改。所以操作资源前一定要加一把锁、防止其他线程访问。
Java中的悲观锁有哪些
- synchronized关键字
- 基于Java同步器AQS的各种实现类
synchronized
Java中的关键字、底层由Jvm虚拟机实现的同步机制,通过两条监听器指令:MONITORENTER(进入)、MONITOREXIT(退出)来实现同步效果(代码编译成字节码文件后可看到指令)
synchronized有三种使用方式:
- 修饰静态方法:锁住的是类,该类下创建的所有对象都被锁住
- 修饰实例方法:锁住的是当前对象,当前对象所属类创建的其他对象不受影响
- 修饰代码块(静态代码块、实例代码块):根据代码块所出区域来区别,如代码块在静态方法中,那锁的是整个类、如代码块在实例方法中,那锁住的是当前实例对象。
基于AQS的实现类
AQS全称(AbstractQueuedSynchronizer)。基于Java程序实现的一种抽象队列同步器框架。AQS定义了一个volatile修饰的int类型变量state来控制是否同步,提供一个unsafe实现的原子方法来更新state(也就是更新锁状态,是否上锁)。
基于AQS,Java本身实现了一些同步类。它们都位于java.util.concurrent包下。例如:
- ReentrantLock(可重入锁,AQS体系下用户使用的最多的一个锁)
- ReentrantReadWriteLock(基于ReentrantLock的读写锁,读锁之间共享资源、读写、写写之间互斥资源,读写锁相较于普通的互斥锁并发能力要稍微好些,但使用起来需要考虑锁的切入点)
- StampedLock(基于读写锁优化,对读锁更加细化了一层,但同时使用也更加复杂,用的不多)
- Semaphore(信号量,可用于限流)
- CountDownLatch(可用于计数,一般用于在多线程环境下需要执行固定次数逻辑的地方)。
二、什么是乐观锁?
乐观锁是一种特殊的锁,它认为所有的资源都是安全的,每个线程对资源的操作都是符合预期的,所以它不需要对资源加锁。
那么问题来了,既然乐观锁不会对资源加锁,那它能不能保证线程安全呢?
答案是:可以保证线程安全,且性能比悲观锁还要好。因为乐观锁在操作资源时,会采用一种确认机制来保证所操作资源未被其他线程更改过。这种机制叫做CAS(Compare And Set)机制。
Java中的乐观锁有哪些
Java没有提供可直接使用的乐观锁,不过内置了一些由底层由乐观锁实现的类。例如:java.util.concurrent.atomic下的几个原子类。
如果我们想自己实现乐观锁的话,可以参考上面那些原子类,使用valotile+CAS 的方式实现。
那么valotile是什么呢?CAS又是什么呢?
Valotile
valotile跟synchronized一样,是Java内置的关键字。不过valotile只能修饰变量。valotile主要的作用是保证变量在内存中的可见性、有序性
- 可见性:valotile修饰的变量被修改后,对内存中是立即可见的。举个例子:有两个线程A、B。有一个valotile修饰的变量Y。当线程A对变量Y进行修改后,线程B能够立刻感知。感知到后会重新读取变量Y的最新值放到自己工作内存中进行操作。
- 有序性:我们都知道,Java代码是一行一行根据编写顺序去运行的。看着是顺序执行没错。但是实际底层JVM在执行字节码文件指令时,有些指令并不会依照代码顺序一样执行。因为Java编译器会在编译成字节码的时候为了提高性能会对你的代码进行指令优化,这个优化叫做指令重排。这个指令重排在单线程环境下不会有问题,因为不管你怎么重排指令,最后的结果都是期望的。但是如果在多线程环境下,就会有线程安全问题。所以valotile帮我们禁止了指令重排的优化,保证了编译器是严格按照代码编写顺序去编译指令。
Java内存模型
了解CAS之前先来了解一下Java内存模型。Java中的内存模型定义,将内存划分为:主内存和工作内存。Java线程在操作资源时,会将其用到的资源复制一份到线程的私有工作内存中,线程在自己的工作内存中对资源完成操作后,再把资源同步回主内存当中。完成一次资源的操作
CAS(Compare And Set)
CAS可以理解为比较后赋值。举例:两个线程A、B。修改一个共享资源变量Y、根据java内存模型定义,两个线程分别会复制一份资源的副本到各自的工作内存中。AY1、BY1。两个线程修改完后会将AY1、BY1同步回主内存中。
然而,在CAS机制下,两个线程除了复制AY1、BY1到工作内存之外,还会另存一个资源副本AY2、BY2。当线程各自修改完AY1、BY1之后,同步主内存之前,会用AY2、BY2与主内存中的资源Y对比,如果对比一致,则立即更新主内存,如果不一致,则重复上面操作,重新从主内存获取资源、修改、同步。
由于CAS在Java底层是一个原子操作,所以可以保证同步数据回主内存时是线程安全的。这点可以参考sun.misc.Unsafe类。这个类提供了原生的CAS能力,直接调native方法于系统底层交互
Valotile+CAS
上面分别介绍了Valotile和CAS。了解到Valotile是为了保证资源的可见性,任何一个线程修改了资源后。其他线程都能立刻感知并重新获取资源。CAS是保证资源的安全性,由于是原子操作,任何一个线程在修改资源时,都是一体的。其他线程是不可操作的。所以Valotile的特性+CAS的机制就组成了一个完美的乐观锁,既保证了线程安全,对性能影响也不大。Valotile的特性+CAS的机制这种组合也可以叫做:Valotile+原子操作
总结
Java当中锁总共分为悲观锁、乐观锁两大分类,悲观锁使用简单、但对并发性能稍微有影响。乐观锁对并发性能影响不大,但是使用较为复杂。
以上就是关于Java中的锁的一个基本总结!
更加详细的内容期待后续更新……
关于Java中的锁,看这一篇就够了(总结篇)相关推荐
- Java中的锁原理、锁优化、CAS、AQS详解
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:景小财 www.jianshu.com/p/e674ee68 ...
- 一篇blog带你了解java中的锁
前言 最近在复习锁这一块,对java中的锁进行整理,本文介绍各种锁,希望给大家带来帮助. Java的锁 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人 ...
- Java中的锁(转)
Java中的锁 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的线程同步机制)是由synchronized同步 ...
- 一文足以了解什么是 Java 中的锁
作者 | cxuan 责编 | Elle Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述 从线程是否需要对资 ...
- Java中的锁详解篇
文章目录 什么是锁 乐观锁和悲观锁 乐观锁 悲观锁 自旋锁 可重入锁 公平锁和非公平锁 读写锁 共享锁和独占锁 偏向锁.重量级锁和轻量级锁 重量级锁 轻量级锁 偏向锁 三种锁的区别 分段锁 同步锁和死 ...
- java中的锁(一)(锁的介绍)
转载:https://blog.csdn.net/zqz_zqz/article/details/70233767/ 测试结果: 1. 单线程下synchronized效率最高(当时感觉它的效率应该是 ...
- Java中的锁[原理、锁优化、CAS、AQS]
点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:用好Java中的枚举,真的没有那么简单!个人原创+1博客:点击前往,查看更多 作者:高广超 链接:https:/ ...
- Java中的锁原理、锁优化、CAS、AQS详解!
阅读本文大概需要 2.8 分钟. 来源:jianshu.com/p/e674ee68fd3f 一.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 二.锁实现的基本原理 2.1.v ...
- JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用
转载自 http://blog.csdn.net/bigtree_3721/article/details/51296064 JAVA 中无锁的线程安全整数 AtomicInteger,一个提供原子操 ...
最新文章
- 骁龙855在AI性能上真的秒杀麒麟980?噱头而已
- openSUSE install albertlauncher from source files
- c语言如何实现玫瑰花
- python问题解决了吗_Python 问题怎么解决?
- python2和3语法区别_python2和3语法区别
- Java 代码性能优化总结
- 另类DATAGRID数据编辑修改
- 事件mousseenter和mouseover的区别
- Windows下使用C++(Win32SDK)编程无需提权读取硬盘序列号
- my ReadBook_wangluoyingxiaoyucehua / network marketing / wangluoyingxiao
- 使用stm32控制驱动步进电机
- WIN7 安装使用远程服务器管理工具-如DHCP、AD域、HyperV、远程桌面
- Matlab实现滤波器,进行ASK、FSK、多音信号的滤波
- 硬盘的读写速度如何计算
- 调试数字音量等级的方法
- uva11134 -Fabled Rooks
- android英语字典(源代码),android英语字典(内含源码哦)
- FPGA 串口波特率时钟的概念
- 加强我国人工智能国防应用的研究与思考
- 清华吴翼学长——ACM参赛故事
热门文章
- 有关积分的不等式证明
- Gulp项目报错:AssertionError [ERR_ASSERTION]: Task function must be specified
- 嵌入式linux 网络唤醒,GotoHTTP
- win10怎么将计算机放桌面壁纸,win10电脑系统怎么改变桌面壁纸的大小
- 学历对于程序员找工作重要吗?我来分享一下看法
- 模棱两可的生物学概念问题辨析1
- 计算机对人类发展历史的影响,【信息技术对人类社会发展有什么影响?】_人类 发展史_全球新能源网...
- java微信支付v3系列——4.创建订单的封装及使用
- Vim 匹配相同的单词并高亮
- DNS域名劫持的几种解决方法