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等)的更多相关文章

  1. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  2. java基础(3)--详解String

    java基础(3)--详解String 其实与八大基本数据类型一样,String也是我们日常中使用非常频繁的对象,但知其然更要知其所以然,现在就去阅读源码深入了解一下String类对象,并解决一些我由 ...

  3. Linux基础知识之挂载详解(mount,umount及开机自动挂载)

    Linux基础知识之挂载详解(mount,umount及开机自动挂载) 转载自:http://www.linuxidc.com/Linux/2016-08/134666.htm 挂载概念简述: 根文件 ...

  4. JAVA基础知识|java虚拟机(JVM)

    一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...

  5. Java基础知识之锁

    Java中实现锁的方式有多种,并且锁的分类也有很多,这篇文章会从锁分类方面简单介绍各分类的锁的特点. 悲观锁和乐观锁 悲观锁:先假设别人也会对数据就行修改,所以先获得锁再进行操作.一个县城在获得锁之后 ...

  6. [java基础知识]java安装步骤

    jre:  java运行环境.  jre =  java虚拟机 + 核心类库(辅助java虚拟机运行的文件).如果只是运行java程序,只需要安装jre.    jdk: java开发工具集   jd ...

  7. java基础知识——Java的定义,特点和技术平台

    (作者声明:对于Java编程语言,很多人只知道怎么用,却对其了解甚少.我也是其中一员.所以菜鸟的我,去查询了教科书以及大神的总结,主要参考了<Java核心技术>这本神作.现在分享给大家!) ...

  8. 计算机基础知识和tcp详解

    计算机基础知识 作为应用软件开发程序员是写应用软件的,而应用软件必须应用在操作系统之上,调用操作系统接口,由操作系统控制硬件 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软 ...

  9. OpenStack基础知识-tox的详解介绍

    1.tox简介 tox是通用的虚拟环境管理和测试命令行工具.tox能够让我们在同一个Host上自定义出多套相互独立且隔离的python环境,每套虚拟环境中可能使用了不同的 Python 拦截器/环境变 ...

  10. java线程基础知识----SecurityManager类详解

    在查看java Thread源码的时候发现一个类----securityManager,虽然很早就知道存在这样一个类但是都没有深究,今天查看了它的api和源码,发现这个类功能强大,可以做很多权限控制策 ...

随机推荐

  1. convert svn repo to git

    https://john.albin.net/git/convert-subversion-to-git 1. 抓取Log 在linux 上做的,其余是在win上做的. 2. svn co svn:/ ...

  2. 【2018寒假集训Day 7】【最短路径】三种算法的模板

    Luogu单源最短路径模版题 dijkstra #include<cstdio> #include<vector> using namespace std; const int ...

  3. centos7关闭默认firewall,启用iptables

    CentOS 7.0默认使用"firewall"防火墙 一:关闭firewall1.直接关闭防火墙systemctl stop firewalld.service 2.禁止fire ...

  4. 快速入门函数式编程——以Javascript为例

    函数式编程是在不改变状态和数据的情况下使用表达式和函数来编写程序的一种编程范式.通过遵守这种范式,我们能够编写更清晰易懂.更能抵御bug的代码.这是通过避免使用流控制语句(for.while.brea ...

  5. python字符串删除,列表删除以及字典删除的总结

    一:字符串删除  1,字符串本身是不可变的,一个字符串定义以后,对他本身是不能做任何操作的,所以的增删改都是对原字符串拷贝的副本的操作,原来的字符串还是原来的字符串,它本身并没 有变 2,字符串本身是 ...

  6. 针对可变类型的for遍历

    针对可变类型的for遍历 举个例子 lis = [1,6,1, 2, 3,3, 4, 5] for i in lis: lis.remove(i) print(lis) [6, 1, 2, 3, 3, ...

  7. Sublime Text 3 免费注册方法(福利)

    对于使用Sublime Text但是又不愿花钱注册的小伙伴,福利到了,免费注册一下你的Sublime吧. 版本3207: 打开Sublime text,然后点击菜单Help->Enter Lis ...

  8. IDEA启动tomcat报java.net.SocketExceptionsocket closed

    IDEA启动tomcat报java.net.SocketException:socket closed.如图所示   解决方法:打开任务管理器,检查有没有java.exe进程. 关闭了重新启动就好了 ...

  9. ASP.NET Core SignalR :学习消息通讯,实现一个消息通知

    什么是 SignalR 目前我用业余时间正在做一个博客系统,其中有个功能就是评论通知,就是假如A用户评论B用户的时候,如果B用户首页处于打开状态,那么就会提示B用户有未读消息.暂时用SignalR来实 ...

  10. 转:关于JAVA项目中CLASSPATH路径详解

    在dos下编译Java程序,就要用到classpath这个概念,尤其是在没有设置环境变量的时候.classpath就是存放.class等编译后文件的路径. javac:如果当前你要编译的Java文件中 ...