《Java基础知识》Java锁详解(volatile,synchronized等)
volatile:
让变量每次在使用的时候,都从主存中取。
volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。
也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。
1. volatile的可见性:
案例:
- public class TestVolatile {
- private static boolean status = false;
- public static void main(String[] args) throws InterruptedException {
- new Thread(() -> {
- while (!status) {
- }
- }).start();
- Thread.sleep(100L);
- status = true;
- System.out.println("status is " + status);
- }
- }
运行结果:status 状态已经打印,但是线程还在继续执行中。
原因是:每一个线程都会创建自己的变量副本存在寄存器中。主线程将值修改了,其他线程是不知道的。
要让status值变了,能够让其他线程都知晓,可以使用volatile.
涉及线程知识:https://www.cnblogs.com/jssj/p/11432409.html
将上述代码修改一下:
下面的代码
- private static boolean status = false;
改成
- private volatile static boolean status = false;
再次运行程序:线程就停止了。
添加一个额外知识:(困扰我好久的疑问)
将上述代码进行修改:去掉volatile,再循环中加入一行:System.out.println("--------------");
- public class TestVolatile {
- private static boolean status = false;
- public static void main(String[] args) throws InterruptedException {
- new Thread(() -> {
- while (!status) {
- System.out.println("--------------");
- }
- }).start();
- Thread.sleep(1L);
- status = true;
- System.out.println("status is " + status);
- }
- }
运行:
线程正常停止了。 为什么?
看了println源码:
- public void println(String x) {
- synchronized (this) {
- print(x);
- newLine();
- }
- }
发现关键字:synchronized。猜测和它有关。
验证:(在循环里面增加了一个synchronized锁,其他都不变)
- public class TestVolatile {
- static TestVolatile testVolatile = new TestVolatile();
- private static boolean status = false;
- public static void main(String[] args) throws InterruptedException {
- new Thread(() -> {
- while (!status) {
- synchronized (testVolatile){
- }
- }
- }).start();
- Thread.sleep(100L);
- status = true;
- System.out.println("status is " + status);
- }
- }
运行结果:
线程正常停止。
同时也证明了:synchronized 也有可见性。开心^-^!
2. volatile的非原子性:
- public class Counter {
- private volatile int inc = 0;
- private void increase() {
- inc++;
- }
- public static void main(String[] args) throws InterruptedException {
- Counter counter = new Counter();
- for (int i = 0; i < 10; i++) {
- new Thread(() -> {
- for (int j = 0; j < 1000; j++) {
- counter.increase();
- }
- }).start();
- }
- Thread.sleep(3000L);
- System.out.println(counter.inc);
- }
- }
运行结果:(每次运行结果不一样的)
原因是:虽然volatile有可见性,但是主存中的变量值和每一个线程寄存器中的变量在某一时刻存在不一致的情况。
图解:(volatile的变量会去主存中获取值,但不一定是最新的),得到结论,volatile不会导致线程阻塞。
synchronized:
Java语言的关键字,当它采用修饰一个方法或一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
- public class MyLockTest implements Runnable {
- private static int index = 1000;
- @Override
- public void run() {
- while(true) {
- if(index > 0){
- try {
- //为了让效果更佳明显
- Thread.sleep(1L);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- index--;
- System.out.println(Thread.currentThread().getName()+"; index = " + index);
- }else{
- break;
- }
- }
- }
- public static void main(String[] args) {
- MyLockTest test = new MyLockTest();
- for (int i = 0; i < 10; i++) {
- new Thread(test, "thread-" + i).start();
- }
- System.out.println("index = "+index);
- }
- }
运行结果:
1. synchronized 原子性
- public class MyLockTest implements Runnable {
- private static int index = 1000;
- @Override
- public void run() {
- while(true) {
- synchronized (this){
- if(index > 0){
- try {
- //为了让效果更佳明显
- Thread.sleep(1L);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- index--;
- System.out.println(Thread.currentThread().getName()+"; index = " + index);
- }else{
- break;
- }
- }
- }
- }
- public static void main(String[] args) {
- MyLockTest test = new MyLockTest();
- for (int i = 0; i < 10; i++) {
- new Thread(test, "thread-" + i).start();
- }
- System.out.println("index = "+index);
- }
- }
运行结果:
synchronized 代码块中的代码就变成一个原子不可分割,必须全部执行完,才能让下一个线程调用。
从中发现执行速度下降非常明显,执行变得异常慢。
2. 可见性上面已经验证过:
3. 无序性,哪个线程执行,资源靠抢(案列中一直被3这个线程占着,每次效果不一)。
下面看看ReentrantLock 锁,可以做到有序,公平,每一个线程先到先得,效率更低。
ReentrantLock 锁
- import java.util.concurrent.locks.ReentrantLock;
- public class MyLockTest implements Runnable {
- private static int index = 1000;
- private ReentrantLock reentrantLock = new ReentrantLock(true);
- @Override
- public void run() {
- while(true) {
- reentrantLock.lock();
- if(index > 0){
- try {
- //为了让效果更佳明显
- Thread.sleep(1L);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- index--;
- System.out.println(Thread.currentThread().getName()+"; index = " + index);
- }else{
- break;
- }
- reentrantLock.unlock();
- }
- }
- public static void main(String[] args) {
- MyLockTest test = new MyLockTest();
- for (int i = 0; i < 10; i++) {
- new Thread(test, "thread-" + i).start();
- }
- System.out.println("index = "+index);
- }
- }
运行结果:
总结:
可见性:volatile ,synchronized ,ReentrantLock
原子性:synchronized ,ReentrantLock
有序性:ReentrantLock
效率(高到低):volatile ,synchronized ,ReentrantLock
Java中还有其他锁,后续有机会补充。
参考:https://blog.csdn.net/wo541075754/article/details/82144223
参考:https://www.cnblogs.com/hustzzl/p/9343797.html
《Java基础知识》Java锁详解(volatile,synchronized等)的更多相关文章
- Java基础-面向接口编程-JDBC详解
Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...
- java基础(3)--详解String
java基础(3)--详解String 其实与八大基本数据类型一样,String也是我们日常中使用非常频繁的对象,但知其然更要知其所以然,现在就去阅读源码深入了解一下String类对象,并解决一些我由 ...
- Linux基础知识之挂载详解(mount,umount及开机自动挂载)
Linux基础知识之挂载详解(mount,umount及开机自动挂载) 转载自:http://www.linuxidc.com/Linux/2016-08/134666.htm 挂载概念简述: 根文件 ...
- JAVA基础知识|java虚拟机(JVM)
一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...
- Java基础知识之锁
Java中实现锁的方式有多种,并且锁的分类也有很多,这篇文章会从锁分类方面简单介绍各分类的锁的特点. 悲观锁和乐观锁 悲观锁:先假设别人也会对数据就行修改,所以先获得锁再进行操作.一个县城在获得锁之后 ...
- [java基础知识]java安装步骤
jre: java运行环境. jre = java虚拟机 + 核心类库(辅助java虚拟机运行的文件).如果只是运行java程序,只需要安装jre. jdk: java开发工具集 jd ...
- java基础知识——Java的定义,特点和技术平台
(作者声明:对于Java编程语言,很多人只知道怎么用,却对其了解甚少.我也是其中一员.所以菜鸟的我,去查询了教科书以及大神的总结,主要参考了<Java核心技术>这本神作.现在分享给大家!) ...
- 计算机基础知识和tcp详解
计算机基础知识 作为应用软件开发程序员是写应用软件的,而应用软件必须应用在操作系统之上,调用操作系统接口,由操作系统控制硬件 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软 ...
- OpenStack基础知识-tox的详解介绍
1.tox简介 tox是通用的虚拟环境管理和测试命令行工具.tox能够让我们在同一个Host上自定义出多套相互独立且隔离的python环境,每套虚拟环境中可能使用了不同的 Python 拦截器/环境变 ...
- java线程基础知识----SecurityManager类详解
在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...
随机推荐
- myBaits持久性框架
动态 SQL 博客交流群:1018996617 动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL ...
- HTML 空元素(转)
HTML 空元素 在 HTML 中,通常在一个空元素上使用一个闭标签是无效的.例如,<input type="text"> </input> 的闭标签是无效 ...
- @NotEmpty、@NotNull、@NotBlank注解解析
源码解析 @NotEmpty根据JDK源码注释说明,该注解只能应用于char可读序列(可简单理解为String对象),colleaction,map,array上,因为该注解要求的是对象不为null且 ...
- solr 的安装和配置
Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引:也可以通过Http Get操 ...
- Solr搜索引擎【索引管理】
一.索引存储 当文档提交到索引之后,directory目录组件会将它们写入到持久化存储器.Solr的目录组件具有以下重要特点: 1.隐藏持久存储的读写细节,例如,将文档写入到磁盘或通过JDBC在数据库 ...
- 基本shell脚本
#!/bin/bash attr=() num= while true do read -p ">>input:" name attr[$num]=$name echo ...
- make命令和makefile
make命令和Makefiles: 1. make是一个命令,解释makefile中指令的命令工具,不同的IDE有自己的make命令. 1. make命令不知道怎么去构建程序,必须有一个文件告诉mak ...
- Spring boot如何快速的配置多个Redis数据源
简介 redis 多数据源主要的运用场景是在需要使用多个redis服务器或者使用多个redis库,本文采用的是fastdep依赖集成框架,快速集成Redis多数据源并集成lettuce连接池,只需引入 ...
- Magicodes.Sms短信库的封装和集成
简介 Magicodes.Sms是心莱团队封装的短信服务库,已提供Abp模块的封装. Nuget 新的包 名称 说明 Nuget Magicodes.Sms.Aliyun 阿里云短信库 Magicod ...
- Linux下基于Docker部署.Net Core web api项目
Docker的好处我就不说啦,问问度娘就知道了