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

用asp.net做企业网站交换友情链接的渠道

用asp.net做企业网站,交换友情链接的渠道,合肥做网站公司有哪些,如何建设一个普通网页网站写在开头 在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码…

写在开头

在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码块,方法参数,局部变量以及实例常量,类常量多不能进行修饰。

自JDK1.5之后,官网对volatile进行了语义增强,这让它在Java多线程领域越发重要!因此,我们今天就抽一晚上时间,来学一学这个关键字,首先,我们从标题入手,思考这样的一个问题:

volatile是如何保证可见性的?又是如何禁止指令重排的,它为什么不能实现原子性呢?

带着疑问,我们一起走进volatile的世界,探索它与可见性,有序性,原子性之间的爱恨情仇!

volatile如何保证可见性?

volatile保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了共享变量的值,共享变量修改后的值对其他线程立即可见。

我们先通过之前写的一个小案例来感受一下什么是可见性问题:

【代码示例1】

public class Test {//是否停止 变量private static boolean stop = false;public static void main(String[] args) throws InterruptedException {//启动线程 1,当 stop 为 true,结束循环new Thread(() -> {System.out.println("线程 1 正在运行...");while (!stop) ;System.out.println("线程 1 终止");}).start();//休眠 1 秒Thread.sleep(1000);//启动线程 2, 设置 stop = truenew Thread(() -> {System.out.println("线程 2 正在运行...");stop = true;System.out.println("设置 stop 变量为 true.");}).start();}
}

输出:

线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.

原因:
我们会发现,线程1运行起来后,休眠1秒,启动线程2,可即便线程2把stop设置为true了,线程1仍然没有停止,这个就是因为 CPU 缓存导致的可见性导致的问题。线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。
在这里插入图片描述
那这个问题怎么解决呢?很好解决!我们排volatile上场可以秒搞定,只需要给stop变量加上volatile修饰符即可!

【代码示例2】

//给stop变量增加volatile修饰符
private static volatile boolean stop = false;

输出:

线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.
线程 1 终止

从结果中看,线程1成功的读取到了线程而设置为true的stop变量值,解决了可见性问题。那volatile到底是什么让变量在多个线程之间保持可见性的呢?请看下图!
在这里插入图片描述
如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取,具体实现可总结为5步。

  • 1️⃣在生成最低成汇编指令时,对volatile修饰的共享变量写操作增加Lock前缀指令,Lock 前缀的指令会引起 CPU 缓存写回内存;
  • 2️⃣CPU 的缓存回写到内存会导致其他 CPU 缓存了该内存地址的数据无效;
  • 3️⃣volatile 变量通过缓存一致性协议保证每个线程获得最新值;
  • 4️⃣缓存一致性协议保证每个 CPU 通过嗅探在总线上传播的数据来检查自己缓存的值是不是修改;
  • 5️⃣当 CPU 发现自己缓存行对应的内存地址被修改,会将当前 CPU 的缓存行设置成无效状态,重新从内存中把数据读到 CPU 缓存。

volatile如何保证有序性?

在之前的学习我们了解到,为了充分利用缓存,提高程序的执行速度,编译器在底层执行的时候,会进行指令重排序的优化操作,但这种优化,在有些时候会带来 有序性 的问题。

那何为有序性呢?我们可以通俗理解为:程序执行的顺序要按照代码的先后顺序。 当然,之前我们还说过发生有序性问题时,我们可以通过给变量添加volatile修饰符进行解决。

首先,我们来回顾一下之前写的一个关于有序性问题的测试类。
【代码示例1】

int a = 1;(1)
int b = 2;(2)
int c = a + b;(3)

上面的这段代码中,c变量依赖a,b的值,因此,在编译器优化重排时,c肯定会在a,b赋值以后执行,但a,b之间没有依赖关系,可能会发生重排序,但这种重排序即便到了多线程中依旧不会存在问题,因为即便重排对执行结果也无影响。

但有些时候,指令重排序可以保证串行语义一致,但是没有义务保证多线程间的语义也一致,我们继续看下面这段代码:

【代码示例2】

public class Test {private static int num = 0;private static boolean ready = false;//禁止指令重排,解决顺序性问题//private static volatile boolean ready = false;public static class ReadThread extends Thread {@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {if (ready) {//(1)System.out.println(num + num);//(2)}System.out.println("读取线程...");}}}public static class WriteRead extends Thread {@Overridepublic void run() {num = 2;//(3)ready = true;//(4)System.out.println("赋值线程...");}}public static void main(String[] args) throws InterruptedException {ReadThread rt = new ReadThread();rt.start();WriteRead wr = new WriteRead();wr.start();Thread.sleep(10);rt.interrupt();System.out.println("rt stop...");}
}

我们定义了2个线程,一个用来求和操作,一个用来赋值操作,因为定义的是成员变量,所以代码(1)(2)(3)(4)之间不存在依赖关系,在运行时极可能发生指令重排序,如将(4)在(3)前执行,顺序为(4)(1)(3)(2),这时输出的就是0而不是4,但在很多性能比较好的电脑上,这种重排序情况不易复现。
这时,我们给ready 变量添加一个volatile关键字,就成功的解决问题了。

volatile关键字可以禁止指令重排的原因主要有两个!

一、3 个 happens-before 规则的实现

  1. 对一个 volatile 变量的写 happens-before 任意后续对这个 volatile 变量的读;
  2. 一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作;
  3. happens-before 传递性,A happens-before B,B happens-before C,则 A happens-before C。

二、内存屏障
变量声明为 volatile 后,在对这个变量进行读写操作的时候,会通过插入特定的 内存屏障 的方式来禁止指令重排序。

内存屏障(Memory Barrier 又称内存栅栏,是一个 CPU 指令),为了实现volatile 内存语义,volatile 变量的写操作,在变量的前面和后面分别插入内存屏障;volatile 变量的读操作是在后面插入两个内存屏障。

具体屏障规则:

  1. 在每个 volatile 写操作的前面插入一个 StoreStore 屏障;
  2. 在每个 volatile 写操作的后面插入一个 StoreLoad 屏障;
  3. 在每个 volatile 读操作的后面插入一个 LoadLoad 屏障;
  4. 在每个 volatile 读操作的后面插入一个 LoadStore 屏障。

屏障说明:

  1. StoreStore:禁止之前的普通写和之后的 volatile 写重排序;
  2. StoreLoad:禁止之前的 volatile 写与之后的 volatile 读/写重排序;
  3. LoadLoad:禁止之后所有的普通读操作和之前的 volatile 读重排序;
  4. LoadStore:禁止之后所有的普通写操作和之前的 volatile 读重排序。

OK,知道了这些内容之后,我们再回头看代码示例2中,增加了volatile关键字后的执行顺序,在赋值线程启动后,执行顺序会变成(3)(4)(1)(2),这时打印的结果就为4啦!

volatile为什么不能保证原子性?

我们讲完了volatile修饰符保证可见性与有序性的内容,接下来我们思考另外一个问题,它能够保证原子性吗?为什么?我们依旧通过一段代码去证明一下!

【代码示例3】

public class Test {//计数变量static volatile int count = 0;public static void main(String[] args) throws InterruptedException {//线程 1 给 count 加 10000Thread t1 = new Thread(() -> {for (int j = 0; j <10000; j++) {count++;}System.out.println("thread t1 count 加 10000 结束");});//线程 2 给 count 加 10000Thread t2 = new Thread(() -> {for (int j = 0; j <10000; j++) {count++;}System.out.println("thread t2 count 加 10000 结束");});//启动线程 1t1.start();//启动线程 2t2.start();//等待线程 1 执行完成t1.join();//等待线程 2 执行完成t2.join();//打印 count 变量System.out.println(count);}
}

我们创建了2个线程,分别对count进行加10000操作,理论上最终输出的结果应该是20000万对吧,但实际并不是,我们看一下真实输出。

输出:

thread t1 count 加 10000 结束
thread t2 count 加 10000 结束
14281

原因:
Java 代码中 的 count++并非原子的,而是一个复合性操作,至少需要三条CPU指令:

  • 指令 1:把变量 count 从内存加载到CPU的寄存器
  • 指令 2:在寄存器中执行 count + 1 操作
  • 指令 3:+1 后的结果写入CPU缓存或内存

即使是单核的 CPU,当线程 1 执行到指令 1 时发生线程切换,线程 2 从内存中读取 count 变量,此时线程 1 和线程 2 中的 count 变量值是相等,都执行完指令 2 和指令 3,写入的 count 的值是相同的。从结果上看,两个线程都进行了 count++,但是 count 的值只增加了 1。这种情况多发生在cpu占用时间较长的线程中,若单线程对count仅增加100,那我们就很难遇到线程的切换,得出的结果也就是200啦。

要想解决也很简单,利用 synchronized、Lock或者AtomicInteger都可以,我们在后面的文章中会聊到的,请继续保持关注哦!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在这里插入图片描述

http://www.ds6.com.cn/news/29584.html

相关文章:

  • 徐州h5建站模板个人博客网页设计
  • 广东h5网站建设steam交易链接在哪复制
  • 湘潭找个人做网站的网站优化搜索排名
  • 南阳做玉器网站北京网络优化
  • 网页设计公司宣传宁波seo企业推广
  • 建设文明网站包括建个网站费用多少
  • 网站建设公司北京亦庄榆林seo
  • 网站由哪些部分组成部分组成部分组成西安seo工作室
  • 深圳网站制作运营市场营销计划书模板
  • 企业网站建设的一般要素主要包括网站的优化方案模板
  • 杭州互助盘网站开发外贸网站模板
  • 商丘网站建设推广软文范例100字
  • 安顺北京网站建设企业营销策划实训报告
  • 做b2b网站的公司山西seo顾问
  • 网站的图片怎么做无法下载广州线下教学
  • 网站链接怎么做标记厦门小鱼网
  • 网站开发方法有哪些鸣蝉智能建站
  • 好看的网站首页设计网站优化查询代码
  • 网站建设怎么付费请输入搜索关键词
  • 群晖搭建企业网站关键词排名优化公司推荐
  • 做网站如何避免侵权武汉久都seo
  • 秒速网站建设如何设计网站的首页
  • iis7.5 部署网站怎么看百度指数
  • 赚钱网站平台推广方案设计
  • 北京网站设计培训机构推广seo网站
  • 网站设计宽屏百度推广如何办理
  • 高端网站建设费用网络推广的概念
  • 广州微网站开发重庆人力资源和社会保障网官网
  • 做网站会员金字塔系统黄冈网站推广软件视频下载
  • 微信公众平台小程序怎么用朝阳区seo