目录

实例:不可见性

volatile:易变关键字

原子性和可见性

问:在之前示例+System.out.println(),会发现不用volatile,线程t1能读取到主内村run变量,原因?

终止模式

打断标记方法:

用volatile方法:

犹豫模式

例子:保证监控线程启动一次

有序性

指令重排:

例子:

利用volatile怎样保证有序性

第二次判断锁的问题:以单例模式为例(懒惰)

解决方案:

happens-before规则

介绍:

第二个规则:

第三个规则:

第四个规则:

第五个规则:

第六个规则:

第七个规则:具有传递性


实例:不可见性

package com.example.juc.JMM;import lombok.extern.slf4j.Slf4j;import static java.lang.Thread.sleep;/*** @author diao 2022/4/13*/
@Slf4j(topic = "c.Test01")
public class Test01 {static boolean run=true;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{while(run){log.debug("开始跑线程");}},"t1");t1.start();sleep(1);log.debug("开始修改,不能跑了");run=false;log.debug("修改完毕");}
}

 每次读取run的值时,后面读取会在自己的工作内存中读取缓存

然后主线程对主内内存的run值进行修改,但是t线程是不知道的——>他读取的还是他的工作内存中的缓存;——>一个线程对值进行修改,另一个线程不知道 ,造成不可见性

解决一:

volatile:易变关键字

他可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,

必须到主存中获取它的值,volatile变量都是直接操作主存的;

package com.example.juc.JMM;import lombok.extern.slf4j.Slf4j;import static java.lang.Thread.sleep;/*** @author diao 2022/4/13*/
@Slf4j(topic = "c.Test01")
public class Test01 {volatile static boolean run=true;public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{log.debug("准备");while(run){log.debug("Running");}},"t1");t1.start();sleep(1);log.debug("开始修改,不能跑了");run=false;log.debug("修改完毕");}
}

解决二:

用synchronized:它会清理线程中的工作内存,去主存中读取数据;(但是是重量级)


原子性和可见性

synchronized即可以保证原子性又可以保证可见性,和有序性;

volatile一般就是保证可见性和有序性;

场景:像那种代码交错的就是原子性有关,关于锁资源的竞争;

而可见性:是其他线程把变量改了在主内存中,而另一个变量因为读取工作内存所以不知道变量更新,所以要volatile;

问:在之前示例+System.out.println(),会发现不用volatile,线程t1能读取到主内村run变量,原因?

因为里面上了synchronized锁;


终止模式

介绍: 在一个线程中结束另一个线程——>优雅的体现:给第二个线程一个处理后事的机会

优雅如何体现:利用isInterrupt()判断线程打断状态

打断标记方法:

根据current.isInterrupted()线程打断状态进行判断,而线程打断利用:Thread.interrupt();

package com.example.juc.JMM;import lombok.extern.slf4j.Slf4j;/*** @author diao 2022/4/14*/
@Slf4j(topic = "c.Test02")
public class Test02 {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();Thread.sleep(2000);log.debug("停止监控");tpt.stop();}}@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{//监控线程private Thread monitorThread;//启动监控线程public void start(){monitorThread=new Thread(()->{log.debug("进入t1线程");while(true){Thread current = Thread.currentThread();//2.1判断线程是否被打断if(current.isInterrupted()){log.debug("料理后事");break;}try{log.debug("开始睡眠");//2.2睡眠,如果被打断就会进入catch,falseThread.sleep(1000);log.debug("执行监控记录");}catch (InterruptedException e){//2.3打断log.debug("当前线程打断");//打断,改变线程状态为true,后面再次进入循环就会为truecurrent.interrupt();log.debug("当前线程状态[{}]",current.isInterrupted());}}},"monitor");monitorThread.start();}//停止监控线程public void stop(){log.debug("打断");monitorThread.interrupt();}}

用volatile方法:

设置一个变量,一个线程修改变量;

package com.example.juc.Balking;import lombok.extern.slf4j.Slf4j;/*** @author diao 2022/4/14*/
//保证监控线程只启动一次
@Slf4j(topic = "c.Test01")
public class Test01 {/**思路:* 1.设置一个变量用volatile修饰,保证它是主内存中修改,这样就可以给多个线程进行判断要不要做* 2.synchronized对临界区保证原子性*/public static void main(String[] args) throws InterruptedException {final TwoTermination t = new TwoTermination();t.start();Thread.sleep(1000);t.stop();}}@Slf4j(topic = "c.TwoTermination")
class TwoTermination{//1.监控线程private Thread monitorThread;//2.停止标记private volatile boolean stop=false;//3.判断是否执行过start方法,双重验证private volatile boolean starting=false;//4.启动监控线程public void start(){log.debug("开始进入线程");monitorThread=new Thread(()->{//防止虚假修改while(true){//当前线程状态Thread current=Thread.currentThread();//判断是否打断if(stop){log.debug("料理后事");break;}try{log.debug("sleeping..");Thread.sleep(1000);log.debug("执行监控记录");}catch(InterruptedException e){current.interrupt();}}},"monitor");monitorThread.start();}public void stop(){log.debug("开始修改...");stop=true;log.debug("修改完毕...");//打断Monitor线程,状态ifalse,会把sleep后面的执行完monitorThread.interrupt();log.debug("修改线程状态完成{}",monitorThread.isInterrupted());}
}

区别:

Interrupt()打断,如果是sleep()睡眠时,打断后线程打断状态为false,默认也是false状态,其余状态被打断则为true;


犹豫模式

介绍:Balking模式主要关注的是状态,当线程发现某个对象状态改变了,则会放弃当前工作,比如word文档中编辑的自动保存,比如:需要初始化系统全局资源,一旦初始化,就不再加载,例如单例

(12条消息) 单例模式的思考_Fairy要carry的博客-CSDN博客

Balking:一个线程发现其他线程做了同一件相同的事情,就无需再做,直接结束返回;

核心:volatile

其实本质跟单例模式很像:有其他线程创建了对象,那么就返回之前的对象,保证了对象的唯一性,加快了效率;


例子:保证监控线程启动一次

1.保护代码,变量验证:

当两个线程t1,t2:t1先进入判断为false,但是此时还没有对starting进行更改,然后t2就进来了,判断为false,然后t1再更改starting状态为true,这样明显就拦不住了;

starting+volatile:没有用,该判断什么样还是啥样,只是能取到改变后准确的值;

要用synchronized:等t1线程改完走完再进去(我们最好上双重验证);——>因为涉及临界区读写操作,要保证原子性;

2.而上面stop+volatile:保证可见性,一个线程修改状态后,另一个线程能拿到;


有序性

指令重排:

多线程下,指令的位置不一样会有明显区别——>比如田忌赛马;

 提示:重排列的指令不能影响结果;

如果指令之间有关联,那么指令不能重排序

例子:

第一种:当先执行actor2:num=2,ready=true都执行然后再进入actor1执行时r1=4

第二种:如果是先num=2,ready还没走完就执行actor1,那么r1=1

如果指令重排:我们将num=2与ready=true进行位置调换

第三种:那么试想,如果先执行ready=true,然后就直接进入actor1方法中,那么num还是=0,所以r1=0

因为此时有num=2并没有执行;

解决:在变量前面加一个volatile即可,可以保证之前的变量同步到主内村中;

利用volatile怎样保证有序性

写屏障:写屏障是写前,并且他不会将屏障之前的代码写在屏障之后;

对volatile修饰的变量进行修改,那么屏障之前的,对共享变量的改动都会同步到主存当中;

 读屏障:读屏障是读后

对volatile修饰的变量进行读取,会保证读屏障后,对共享变量的读取,加载都是主存中最新数据;

有序性:这个只是保证了当前线程的代码有序性,防止指令重排带来的影响;


第二次判断锁的问题:以单例模式为例(懒惰)

1.当我们对资源上锁,目的是所有线程共用一个对象,所以我们进行上锁

2.但是我们只需要第一次需要:进行互斥判断——>因为之后的进来判断,肯定都!=null,所以只需要第一次上锁就行;

进行修改:先判断是否为空,为空说明是第一次,第一次进去才同步

2. 但还是有问题:因为INSTANCE在外面,也就是其他线程可以拿到INSTANCE变量,

3. 但是这里是有问题的,它可以先赋值,再调用无参构造(之前的是:先调用无参构造,再进行赋值)

这里就会出现一个问题,因为我们锁的是创建对象的区域,而上面INSTANCE并没有锁,所以说,当第一个线程先进行赋值,再还没有调用无参构造的时候,第二个线程加入,进行INSTANCE的判断(这就产生指令交错了,一个以INSTANCE为基准的指令交错),而第二个线程拿到的变量就是没有调用无参构造就赋值的变量,(也就是说出现了重排列现象);——>创建了,但又和没创建一样,也就是说Instance有内存地址,但是没有被初始化;

t2拿到的就是没有创建好的变量;

解决方案:

我们可以给Instance加volatile,也就是说保证了代码的有序性,防止指令重排——>给写读操作加上了写屏障和读屏障;

写:在我们给Instance赋值的时候就是写操作,之前的读操作(也就是调用构造方法),它不会因为指令重排而到Instance赋值后面,也就是说咋上了一道写屏障;


 问:为什么只要把代码块放到synchronized中就可以保证原子性,可见性和有序性呢?

因为所有代码放到synchronized同步代码块中,说明已经是单线程的场景了,就算你是发现了重排序,代码执行的结果也是一样的;


happens-before规则

介绍:

规定了共享变量的写操作对其他线程的读操作是可见的——>(可见性与有序性的一套规则)

因为synchronized会将工作内存清除,所以说读的话是读的主内存中的内容

第二个规则:

volatile:保证了其他线程对变量的读可见

 第三个规则:

在线程start前,对变量进行写,因为你这次写是同步到主内存中,你线程一调用第一次肯定是主内存的内容,然后加载到工作内存中——>除非你在线程已经在工作的时候,修改了x的值,这时候由于线程读取的是缓存内存中的内容,所以会造成内容读取不一致;

 第四个规则

当线程结束后,会同步内容

 第五个规则:

线程打断前后变量的可见——>t1打断t2前时,对于变量的读可见性以及打断后变量的读可见性;

 第六个规则:

对于默认值(0,false,null)的写,其他线程对该变量的读可见;

而默认值指的就是:类加载过程中,准备阶段,赋的默认值;

(12条消息) 类加载机制(一些简单的理解)——阶段性学习_Fairy要carry的博客-CSDN博客

第七个规则:具有传递性

其实只要我们的写操作在读操作前就有读可见,除非你是线程已经读取过该变量了,然后变量被其他线程修改,那么你再读取该变量,就出问题了;

可见性与有序性和原子性01相关推荐

  1. 3.线程安全之可见性、有序性、原子性是什么?

    小陈:上一篇说了JAVA内存模型,但是后面说了在多线程并发操作的时候有可见性问题,我现在迫不及待想知道线程安全的可见性.原子性.有序性是啥了 老王:哈哈,可以.我先说说我自己对可见性.有序性.原子性的 ...

  2. 通俗理解java的可见性、有序性和原子性

    话不多说,先上一张图 没错,我们今天聊的东西,跟他没啥关系. 上面这是java的内存结构(我就是忽悠你们来的). 今儿主要先聊一聊java的内存模型(嗯,也不是非想跟你们聊,主要是标题得从这玩意儿来引 ...

  3. c++的可见性,有序性与原子性

    c++的可见性,有序性与原子性 1.可见性   可见性指的当一个线程修改某个变量时,另一个线程也可以看到该变量的值的变化.但是由于编译器的优化或者cpu寄存器以及cache的硬件结构,有可能某个线程改 ...

  4. volatile关键字——保证并发编程中的可见性、有序性

    文章目录 一.缓存一致性问题 二.并发编程中的三个概念 三.Java线程内存模型 1.原子性 2.可见性 3.有序性 四.深入剖析volatile关键字 1.volatile关键字的两层语义 2.vo ...

  5. 7.volatile怎么通过内存屏障保证可见性和有序性?

    volatile通过内存屏障保证可见性 小陈:老王,你上一篇抛出一个问题volatile怎么通过内存屏障保证可见性和有序性?我现在迫不及待的想知道了. 老王:嗯嗯,我们慢慢来讲,先说说volatile ...

  6. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  7. volatile对原子性、可见性、有序性的保证

    1.原子性 volatile对原子性的保障有限,32位jvm中的long.double类型的变量赋值操作不是原子的,volatile可以保证它俩的原子性. 2.可见性.有序性 volatile在写操作 ...

  8. synchronized同时对原子性、可见性、有序性的保证

    原子性:基本复制写操作都能保证原子性,复杂操作无法保证 可见性:MESI协议的flush.refresh配合使用,解决可见性 有序性:3个层次,最后1个层次有4中内存重排序 synchronized可 ...

  9. JMM中的原子性、可见性、有序性和volatile关键字

    相信如果对JMM底层有过了解或者接触过java并发编程的读者对以上的概念并不陌生,但是真正理解的可能并不多.这里我就对这些概念再做一次讲解.相信读者多读几遍应该就有自己的理解,实在不理解也没关系,说明 ...

最新文章

  1. 移动应用程序和网页应用程序_您的移动应用程序运行缓慢的主要原因以及如何修复它...
  2. 比起睡觉,我更喜欢刷巨详细的Java枚举类,这是来自猿人的自觉呀
  3. 软件工程概论作业:返回一个整数数组中最大子数组的和
  4. menuItem无法响应点击事件
  5. CLI4 去掉严格模式
  6. 脑电信号特征提取算法c语言_应用深度学习EEGNet来处理脑电信号
  7. 19行代码AC——例题 6-2 铁轨(Rails, UVa 514)——解题报告
  8. 批量删除Marketing Cloud里的contact
  9. MYSQL性能优化分享(分库分表)
  10. Java基础知识之方法的返回值与重载
  11. SqlServer高级存储过程
  12. Tomcat SSL Configuration
  13. linux打开word、excel等
  14. 北斗导航:太空中最亮的“中国星”
  15. SWI-Prolog的下载与使用
  16. 云计算是什么?云计算基础概念讲解
  17. 电源芯片选择DC/DC还是LDO?
  18. 润和软件全面参与OLA联盟智能家居专委会举办第三次联调测试活动
  19. [附源码]Python计算机毕业设计Django的手机电商网站
  20. vue搜索、历史记录功能简单实现

热门文章

  1. AC-DC数字稳压电源设计与仿真+Multisim仿真
  2. POJ | 1017装箱问题 摸鱼题解
  3. 知乎周源微信_每周源代码42-树修剪,插件和MEF
  4. 半导体存储芯片及译码驱动方式
  5. 【圣诞系列】注意:这款Python版创意十足的专属“圣诞二维码”上线啦(赶紧收藏)
  6. 金仓数据库KingbaseES之存储过程的RETURN语句
  7. 思科认证ccna考试费是多少?考证书容易吗?能找到工作吗?
  8. Python中的判断与循环语句
  9. 毕业生必知二三事。给即将毕业的师弟师妹看看,很有用的~~~~
  10. 时间(秒)转时间字符串(x天x时x分x秒)