当前位置: 首页 > news >正文

九江网站设计公司表白网页制作免费网站制作

九江网站设计公司,表白网页制作免费网站制作,橙象品牌设计,深圳朝阳电子网站建设Java 内存模型 JMM(java memory model )它从java层面定义了主存,工作内存抽象概念。底层对应着CPU寄存器,缓存,硬件内存,CPU指令优化等。JMM体现如下方面 原子性 :保证指令不会受到线程上下文切…

Java 内存模型

JMM(java memory model )它从java层面定义了主存,工作内存抽象概念。底层对应着CPU寄存器,缓存,硬件内存,CPU指令优化等。JMM体现如下方面

  • 原子性 :保证指令不会受到线程上下文切换的影响
  • 可见性 :保证指令不会受到CPU缓存的影响
  • 有序性 :保证指令不会受到CPU指令并优化的影响

原子性

  • 定义:即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性就像数据库里面的事务一样,他们是一个团队,同生共死。

  • 原子操作的描述:多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。也就是原子类操作是不能被线程调度机制中断的操作。

可见性

定义:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的

public class Test1 {static boolean run = true;public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while(run){// ....
//                System.out.println(2323);  如果加上这个代码就会停下来}});t.start();utils.sleep(1);System.out.println(3434);   run = false; // 线程t不会如预想的停下来}
}

以上案例main 线程对 run 变量的修改对于 t 线程不可见,导致了 t 线程无法停止,陷入了无限循环

分析

初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存。
在这里插入图片描述
因为 t 线程要频繁从主内存中读取 run 的值,JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中, 减少对主存中 run 的访问,提高效率
在这里插入图片描述
1 秒之后,main 线程修改了 run 的值,并同步至主存,而 t 是从自己工作内存中的高速缓存中读取这个变量 的值,结果永远是旧值
在这里插入图片描述

解决方案

  • volatile(易变关键字)可以用来修饰成员变量和静态成员变量,禁止线程从自己的魂缓存中查找变量的值,必须到主存中获取它的值。(也就是直接从主存中取值)

  • 然而使用synchronized也有同样的效果,在java内存模型中,synchronized规定,线程在加锁的时候,需要先清空工作内存,并在主存中拷贝最新变量的副本到工作内存,在每次执行代码之后将最新的共享变量的值同步刷新到主内存中,释放互斥锁

总结

  • 前面例子所讲的就是可见性,它保证的是多个线程之间,一个线程对应volatille变量的修改对于另一个线程可见,不能保证原子性,仅用一个写线程,多核线程读的情况

  • synchronized既可以保证代码的原子性,也可以保证代码内变量的可见性

  • 缺点就是synchronized是重量级锁,性能较低。

  • 而 System.out.println() 也能保证共享变量的统一,是因为它的底层也使用了synchronized 关键字

public void println(String x) {//使用了synchronized关键字synchronized (this) {print(x);newLine();}}

如何保证可见性

  • 写屏障:保证在该屏障之前的,对共享变量的改动,都同步到主存当中
public void actor2(I_Result r) {num = 2;ready = true; // ready是被volatile修饰的 ,赋值带写屏障// 写屏障
}
  • 读屏障:保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据
public void actor1(I_Result r) {// 读屏障//  ready是被volatile修饰的 ,读取值带读屏障if(ready) {r.r1 = num + num;} else {r.r1 = 1;}
}

在这里插入图片描述

两阶段终止模式

package com.niecun;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.TimeUnit;public class T1 {public static void main(String[] args) throws InterruptedException {Monitor monitor = new Monitor();monitor.start();Thread.sleep(3);monitor.stop();}
}
@Slf4j(topic = "c.Monitor")
class Monitor{private static Thread monitor;private volatile static boolean flag = false;public void start(){monitor = new Thread(() -> {log.debug("监控开启。。。");while (true){if(flag){log.debug("处理事情。。。");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log.debug("处理完毕。。。。");break;}log.debug("监控中。。。");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {log.debug("被打断了");}}}, "monitor");monitor.start();}public void stop() {monitor.interrupt();flag = true;}
}

在这里插入图片描述

模式之 Balking

  • 定义:Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需再做了,直接结束返回。有点类似于单例。
package com.niecun;import lombok.extern.slf4j.Slf4j;
import org.omg.PortableInterceptor.LOCATION_FORWARD;import java.util.concurrent.TimeUnit;public class T2 {public static void main(String[] args) throws InterruptedException {Blaking monitor = new Blaking();monitor.start();monitor.start();Thread.sleep(3);monitor.stop();}
}@Slf4j(topic = "c.Monitor")
class Blaking{private static Thread monitor;private volatile static boolean flag = false;private static boolean isStaring = false;public void start(){monitor = new Thread(() -> {synchronized (this){if (isStaring){log.debug("已被开启");return;}isStaring = true;}log.debug("监控开启。。。");while (true){if(flag){log.debug("处理事情。。。");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}log.debug("处理完毕。。。。");break;}log.debug("监控中。。。");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {log.debug("被打断了");}}}, "monitor");monitor.start();}public void stop() {monitor.interrupt();flag = true;}}

在这里插入图片描述

有序性(指令重排)

JVM 会在不影响正确性的前提下,可以调整语句的执行顺序
在这里插入图片描述
改变如上程序的先后执行顺序,结果不变(这种特性称之为『指令重排』,多线程下『指令重排』会影响正确性。)

比如:在多线程的情况下去执行如下结果

int num = 0;// volatile 修饰的变量,可以禁用指令重排 volatile boolean ready = false; 可以防止变量之前的代码被重排序
boolean ready = false; 
// 线程1 执行此方法
public void actor1(I_Result r) {if(ready) {r.r1 = num + num;} else {r.r1 = 1;}
}
// 线程2 执行此方法
public void actor2(I_Result r) {num = 2;ready = true;
}
  1. num = 0 线程1在执行actor2 执行 ready = true,切换到线程1,进入 if 分支,相加为 0,同时在切换会线程1执行num = 2
  2. num = 1 线程1在执行到actor2中的 num = 2 线程2先执行if判断直接到else 赋值为1
  3. num= 4 正常情况,依次执行

注意:这种现象叫做指令重排,是 JIT 编译器运行时的一些优化,这个现象需要通过大量测试才能复现,可以使用jcstress工具进行测试。

重排序也需要遵守一定规则

  • 重排序操作不会对存在数据依赖关系的操作进行重排序:比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运行时这两个操作不会被重排序。
  • 重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变:比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系,所以可能会发生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。

( 重排序)问题分析

  • 问题:重排序在单线程模式下是一定会保证最终结果的正确性,但是在多线程环境下,无法保证其正确性。
  • 解决方法volatile 修饰的变量,可以禁用指令重排,使用synchronized加锁的操作
  • 注意synchronized虽然也可以,但是该锁为重量级锁,极其消耗性能volatile不能保证原子性

如何保证有序性

  • 写屏障:保证指令重排序时,不会将屏障之前的代码排到屏障之后
public void actor2(I_Result r) {num = 2;ready = true; //  ready是被volatile修饰的 , 赋值带写屏障// 写屏障
}
  • 读屏障:确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
public void actor1(I_Result r) {// 读屏障//  ready是被volatile修饰的 ,读取值带读屏障if(ready) {r.r1 = num + num;} else {r.r1 = 1;}
}

在这里插入图片描述

分析总结(指令有序性)

  1. 不能解决指令交错

  2. 写屏障仅仅是保证之后的读能够读到最新的结果,但不能保证其他线程的读不能跑到他的前面

  3. 故有序性也只是保证了被线程内部的相关代码不被重排序
    在这里插入图片描述

happens-before

定义:A happens-before B就是A先行发生于B,定义为HB(A,B)在java内存模型中,happens-before的意思是前一个操作的结果可以被后续操作获取

  • 为何需要该模式

JVM会对代码进行编译优化,会出现指令重排序的情况,为了避免编译优化对于编程安全性的影响,需要happens-before定义一些禁止编译优化的场景,保证并发编程结果的正确性。

  • happens-before相关规则(详情)
  1. 程序次序规则:在一个线程内一段代码的执行结果是有序的,就是还会指令重排,但是无论怎么排序,结果都不会变化。

  2. 管程锁定规则:就是无论是在单线程环境还是多线程环境,对于同一个锁来说,一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果!(管程是一种通用的同步原语,synchronized就是管程的实现)

  3. volatile变量规则:就是如果一个线程先去写一个volatile变量,然后一个线程去读这个变量,那么这个写操作的结果一定对读的这个线程可见。

  4. 线程启动规则:在主线程A执行过程中,启动子线程B,那么线程A在启动子线程B之前对共享变量的修改结果对线程B可见。

  5. 线程终止规则:在主线程A执行过程中,子线程B终止,那么线程B在终止之前对共享变量的修改结果在线程A中可见。也称线程join()规则。

  6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生,可以通过Thread.interrupted()检测到是否发生中断。

  7. 传递性规则:这个简单的,就是happens-before原则具有传递性,即hb(A, B) , hb(B, C),那么hb(A, C)。

  8. 对象终结规则:这个也简单的,就是一个对象的初始化的完成,也就是构造函数执行的结束一定 happens-before它的finalize()方法。

as-if-serial

解决单线程的可见性问题

http://www.lbrq.cn/news/2614123.html

相关文章:

  • 做网站有效果吗短信营销平台
  • 做网站单线程CPU和多线程cpu公司查询
  • 宜昌网站设计公司网站收录服务
  • 怎么做代刷网站教程网络营销推广公司简介
  • 建设网站的公司广州长沙网络推广小公司
  • 网店代运营正规公司性价比高seo排名优化的
  • cms网站开发需要学什么设计公司取名字大全集
  • 网站开发课程软件seo每日工作
  • 专做进口批发的网站宁波seo外包优化公司
  • 中国网站建设世界排名网站免费下载安装
  • 网站建设文字资料seo综合查询怎么用
  • 农业网站怎么做网络优化主要做什么
  • 做网站需要域名嘉兴seo优化
  • 想自己做点飘纱素材到网站上买网站如何提升seo排名
  • 网页设计与网站建设 设计题100个常用的关键词
  • 这个百度的网站怎么做的成都新一轮疫情
  • 临朐网站制作哪家好semantic scholar
  • 广州建设银行网站首页2022年免费云服务器
  • 福建电信网站备案长春网站seo公司
  • 网站后台链接怎么做高州新闻 头条 今天
  • d?t网站模版温州seo网站建设
  • wordpress没有备案seo优化网络公司
  • erp软件怎么用搜索引擎优化培训中心
  • 网站备案 停站免费html网站模板
  • 威县网站建设无锡网站制作优化
  • 广州中小学安全教育平台企业整站优化
  • 视频源网站怎么做优化方案的格式及范文
  • 做视频网站seo怎么赚钱
  • wordpress发号系统巢湖seo推广
  • 做ui必要的网站最新最好的磁力搜索
  • K8S的POD数量限制
  • 二叉树算法
  • 【Git学习】入门与基础
  • VPS云服务器Linux系统备份策略与灾难恢复方案设计
  • Jmeter进行性能并发测试
  • vllm启动Qwen/Qwen3-Coder-30B-A3B-Instruct并支持工具调用