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

s001网站建设公司主流网站开发语言有哪些

s001网站建设公司,主流网站开发语言有哪些,免费行情软件app网站大全,wordpress 登陆id认识volatile volatile关键字的作用有两个#xff1a;变量修改对其他线程立即可见、禁止指令重排。 第二个作用我们后面再讲#xff0c;先主要讲一下第一个作用。通俗点来说#xff0c;就是我在一个线程对一个变量进行了修改#xff0c;那么其他线程马上就可以知道我修改… 认识volatile volatile关键字的作用有两个变量修改对其他线程立即可见、禁止指令重排。 第二个作用我们后面再讲先主要讲一下第一个作用。通俗点来说就是我在一个线程对一个变量进行了修改那么其他线程马上就可以知道我修改了他。嗯难道我修改了数值其他线程不知道我们先从实例代码中来感受volatile关键字的第一个作用。 private static boolean flag false;public static void main(String[] args) throws InterruptedException {new Thread(() - {while (!flag) {}System.out.println(Flag is now true!);}).start();Thread.sleep(1000);flag true;System.out.println(Flag is set to true!);} 结果 在没有volatile关键字的时候只展示了主线程中flag改变的值并输出结果子线程并没有打印任何信息说明主线程的改变不会对子线程有影响他们之间是不可见的 现在我们加上volatile关键字 private static volatile boolean flag false; 结果 现在主线程的改变子线程也是可以看到flag的变化了说明volatile让两个线程彼此可见了同时也是有顺序的这就是volatile关键字的第一个重要作用变量修改对其他线程立即可见。关于指令重排我们后面再讲。 那么为什么变量的修改是对其他线程不是立即可见呢volatile为何能实现这个效果那这样我们可不可以每个变量都给他加上volatile关键字修饰要解决这些问题我们得先从Java内存模型说起。系好安全带我们的车准备开进底层原理了。 Java内存模型 JVM把内存总体分为两个部分线程私有以及线程共享线程共享区域也称为主内存。线程私有部分不会发生并发问题所以主要是关注线程共享区域。这个图大致上可以这么理解 所有共享变量存储在主内存每条线程拥有自己的工作内存工作内存保留了被该线程使用的变量的主内存副本变量操作必须在工作内存进行不同线程之间无法访问对方的工作内存 简单总结一下所有数据都要放在主内存中线程要操作这些数据必须要先拷贝到自己的工作内存然后只能对自己工作内存中的数据进行修改修改完成之后再写回主内存。线程无法访问另一个线程的数据这也就是为什么线程私有的数据不存在并发问题。 那为什么不直接从主内存修改数据而要先在工作内存修改后再写回主内存呢这就涉及到了高速缓冲区的设计。简单来说处理器的速度非常快但是执行的过程中需要频繁在内存中读写数据而内存访问的速度远远跟不上cpu的速度导致降低了cpu的效率。因而设计出高速缓冲区处理器可以直接操作高速缓冲区的数据等到空闲时间再把数据写回主内存提高了性能。而JVM为了屏蔽不同的平台对于高速缓冲区的设计就设计出了Java内存模型来让开发者可以面向统一的内存模型进行编程。可以看到这里的工作内存就对应硬件层面的高速缓冲区。 指令重排 前面我们一直没讲指令重排是因为这是属于JVM在后端编译阶段进行的优化而在代码中隐藏的问题很难去复现也很难通过代码执行来看出差别。指令重排是即时编译器对于字节码编译过程中的一种优化受到运行时环境的影响。限于能力笔者只能通过理论分析来讲这个问题。 我们知道JVM执行字节码一般有两种方式解释器执行和即时编译器执行。解释器这个比较容易理解就是一行行代码解释执行所以也不存在指令重排的问题。但是解释执行存在很大的问题解释代码需要耗费一定的处理时间、无法对编译结果进行优化所以解释执行一般在应用刚启动时或者即时编译遇到异常才使用解释执行。而即时编译则是在运行过程中把热点代码先编译成机器码等到运行到该代码的时候就可以直接执行机器码不需要进行解释提高了性能。而在编译过程中我们可以对编译后的机器码进行优化只要保证运行结果一致我们可以按照计算机世界的特性对机器码进行优化如方法内联、公共子表达式消除等等指令重排就是其中一种。 计算机的思维跟我们人的思维是不一样的我们按照面向对象的思维来编程但计算机却必须按照“面向过程”的思维来执行。所以在不影响执行结果的情况下JVM会更改代码的执行顺序。注意这里的不影响执行结果是指在当前线程下。如我们可能会在一个线程中初始化一个组件等到初始化完成则设置标志位为true然后其他线程只需要监听该标志位即可监听初始化是否完成如下 // 初始化操作 isFinish true; 在当前线程看起来注意是看起来会先执行初始化操作再执行赋值操作因为结果是符合预期的。但是在其他线程看来这整个执行顺序都是乱的。JVM可能先执行isFinish赋值操作再执行初始化操作而如果你在别的线程监听isFinish变化就可能出现还未初始化完成isFinish却是true的问题。而volatile可以禁止指令重排保证在isFinish被赋值之前所有的初始化动作都已经完成。 volatile的具体定义 上面讲了两个看起来跟我们的主角volatile关系不大的知识点但其实是非常重要的知识点。 首先通过Java内存模型的理解现在知道为什么会出现线程对变量的修改其他线程未立即可知的原因了吧线程修改变量之后可能并不会立即写回主内存而其他线程在主内存数据更新后也并不会立即去主内存获取最新的数据。这也是问题所在。 被volatile关键字修饰的变量规定每次使用数据都必须去主内存中获取每次修改完数据都必须马上同步到主内存。这样就实现了每个线程都可以立即收到该变量的修改信息。不会出现读取脏数据旧数据的情况。 第二个作用是禁止指令重排。这里是使用到了JVM的一个规定同步内存操作前的所有操作必须已经完成。而我们知道每次给volatile赋值的时候他会同步到主内存中。所以在同步之前保证所有操作都必须完成了。所以当其他线程监测到变量的变化时赋值前的操作就肯定都已经完成了。 既然volatile关键字这么好那可不可以每个地方都使用好了当然不行在前面讲Java内存模型的时候有提到为了提高cpu效率才分出了高速缓冲区。如果一个变量并不需要保证线程安全那么频繁地写入和读取内存是很大的性能消耗。因而只有必须使用volatile的地方才使用他。 volatile修饰的变量一定是线程安全吗 首先明确一下怎么样才算是线程安全 一个操作要符合原子性要么不执行要么一次性执行完成在执行过程中不会受其他操作的影响。对于变量的修改相对于其他线程必须立即可见。代码的执行在其他线程看来要满足有序性。 从上面分析我们讲了代码的有序性、修改的可见性但是缺少了原子性。在JVM中在主内存进行读写都是满足原子性的这是JVM保证的原子性那么volatile岂不是线程安全的并不是。 JVM的计算过程并不是原子性的。我们举个例子看一下代码 public class VolatileDemo {private volatile int counter 0;public void increment() {counter;}public int getCounter() {return counter;}public static void main(String[] args) throws InterruptedException {VolatileDemo demo new VolatileDemo();Thread t1 new Thread(() - {for (int i 0; i 1000000; i) {demo.increment();}});Thread t2 new Thread(() - {for (int i 0; i 1000000; i) {demo.increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(Counter: demo.getCounter());} }结果应该是2000000才对让我们看下结果 结果竟然不对 这是因为volatile仅仅只是保证了修改后的数据对其他线程立即可见但是并不保证运算的过程的原子性。 这里的counter在编译之后是分为三步1.在工作区中取出变量数据到处理器 2.对处理器中的数据进行加一操作 3.把数据写回工作内存。如果在变量数据取到处理器运算的过程中变量已经被修改了所以这时候进行自增操作得到的结果就是错误的。举个例子 变量a5,当前线程取5到处理器这时a被其他线程改成了8但是处理器继续工作把5自增得到6然后把6写回主内存覆盖数据8从而导致了错误。因此由于Java运算的非原子性volatile并不是绝对线程安全。 那什么时候他是安全的 计算的结果不依赖原来的状态。不需要与其他的状态变量共同参与不变约束。 通俗点来讲就是运算不需要依赖于任何状态的运算。因为依赖的状态可能在运算的过程中就已经发生了变化了而处理器并不知道。如果涉及到需要依赖状态的运算则必须使用其他的线程安全方案如加锁来保证操作的原子性。 示例 适用场景 状态标志/多线程通知 状态标志是很适合使用volatile关键字正如我们在第一部分举的例子通过设置标志来通知其他所有的线程执行逻辑。或者是如上一部分的例子当初始化完成之后设置一个标志通知其他线程。 而更加常用的一个类似场景是线程通知。我们可以设置一个变量然后多个线程观察他。这样只需要在一个线程更改这个数值那么其他的观察这个变量的线程均可以收到通知。非常轻量级、逻辑也更加简单。 保证初始化完整双重锁检查问题 单例模式是我们经常使用的其中一种比较常见的写法就是双重锁检查如下 public class JavaClass {// 静态内部变量private static JavaClass javaClass;// 构造器私有private JavaClass(){}public static JavaClass getInstance(){// 第一重判空if (javaClassnull){// 加锁第二重判空synchronized(JavaClass.class){if (javaClassnull){javaClass new JavaClass();}}}return javaClass;} } 这种代码很熟悉对吧具体的设计逻辑我就不展开了。那么这样的代码是否绝对是线程安全的呢并不是的在某些极端情况下仍然会出现问题。而问题就出在javaClass new JavaClass();这句代码上。 新建对象并不是一个原子操作他主要有三个子操作 分配内存空间初始化Singleton实例赋值 instance 实例引用 正常情况下也是按照这个顺序执行。但是JVM是会进行指令重排优化的。就可能变成 分配内存空间赋值 instance 实例引用初始化Singleton实例 赋值引用在初始化之前那么外部拿到的引用可能就是一个未完全初始化的对象这样就造成问题了。所以这里可以给单例对象进行volatile修饰限制指令重排就不会出现这种情况了。当然关于单例模式更加建议的写法还是利用类加载来保证全局单例以及线程安全当然前提是你要保证只有一个类加载器。限于篇幅这里就不展开了。 总结 适用volatile的场景一般是初始化标志 不使用volatile的场景涉及指令重排多线程操作数字累加
http://www.zqtcl.cn/news/648832/

相关文章:

  • 网站名称创意大全wordpress公开课插件
  • 淮安市城市建设档案馆网站可以做网页的软件
  • 网站空间服务器wordpress 排除置顶文章
  • 有域名后怎么做网站邯郸做移动网站的地方
  • 商标可以做网站吗网站开发的大学生应届简历
  • 长沙长沙网站建设公司saas系统架构
  • 成都销售型网站长春财经学院多大
  • 手机自己制作表白网站app项目网络计划图怎么画
  • 品牌网站如何做seo浏览器正能量网址
  • 开封做网站哪家好网页设计制作网站大一素材
  • 河南网站域名备案莱芜新闻电视台节目表
  • 长春网站建设新格做天猫还是做网站推广
  • 新网站建设的感想安阳区号是什么
  • 余姚市城乡建设局网站wordpress 预览插件
  • 游戏开发和网站开发wordpress foreign trade
  • 网站设计 原型图html购物网站模板
  • 谷歌网站推广报价国产搜什么关键词最好看
  • 婚礼网站有哪些个人做网站需要什么条件
  • 深圳企业网站seo人才招聘网站建设
  • 谷歌下载seo是什么软件
  • 个人网站设计分析小程序在线制作平台
  • 网站开发 一般用什么语言vi视觉设计案例
  • 微信公众平台官方网官网seo优化找哪家做
  • 简约 网站模板网站目录链接怎么做
  • 国内地铁建设公司网站大连做网站外包
  • 微网站营销是什么网站图片上传代码
  • 外包公司做网站多少用vs做的网站怎么打开
  • 兴义城乡建设部网站企业服务器配置方案
  • 淘宝客网站根目录wordpress调用导航代码
  • 海外免费网站推广网站开发项目报告书