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

网站开发网络课程门户网站架构

网站开发网络课程,门户网站架构,ps外包网站,页面设计实训心得3. 线程安全 线程安全#xff1a;某个代码#xff0c;不管它是单个线程执行#xff0c;还是多个线程执行#xff0c;都不会产生bug#xff0c;这个情况就成为“线程安全”。 线程不安全#xff1a;某个代码#xff0c;它单个线程执行#xff0c;不会产生bug#xff0c…3. 线程安全 线程安全某个代码不管它是单个线程执行还是多个线程执行都不会产生bug这个情况就成为“线程安全”。 线程不安全某个代码它单个线程执行不会产生bug但是多个线程执行就会产生bug这个情况就成为 “线程不安全”或者 “存在线程安全问题”。      举个线程不安全例子,我们计算一个变量的自增次数它循环了100000次用两个线程去计算各自计算循环50000次的次数。    3.1 线程不安全样例 根本原因线程的随机调度抢占式执行 代码结构不同线程修改同一数据 直接原因多线程操作不是原子的 由于线程的随机调度抢占式执行不可避免代码结构会促进该原因加剧不良后果 1、代码一分析----代码随机调度抢占执行的例子         代码如下 public class ThreadDemo4 {private static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {for (int i 1; i 50000; i) {count;}});Thread t2 new Thread(() - {for (int i 50000; i 100000; i) {count;}});t1.start();t2.start();t1.join();t2.join();System.out.println(count: count);} }         按照我们的逻辑从1自增到10_0000肯定是自增了10_0000次但是结果如下图所示                我们实际答案却不是10_0000是53978次其出现如上情况的最主原因就是多线程代码它们是并发执行的且往代码深层次分析java中的count语句是由cpu的三个指令构成的 1首先load 从内存中读取数据到cpu的寄存器中 2其次add 把寄存器中的值 1 3最后save 把寄存器中的值写回到内存中         因为上面两个线程t1和t2是并发执行的那则t1 和 t2 线程的执行顺序就是无序的他们可能同时读取内存中的数据add双方都自增完往寄存器1应该是1后再1但是最后从寄存器中save到内存中时却只读取了一个线程自增完后的数值另外一个自增的过程被忽略了一些具体的分析如下图所示         线程并发执行的结果是无数的并不是简单的排列组合就能穷举出来因为并发的原因可能 t1 线程它执行了两次才执行一次 t2 线程或者 t2 执行的次数更多t1 线程只执行一次。等以上这些情况都是有可能出现的。         故此t1 和 t2自增的时候就可能从寄存器中拿的是同一个值这两线程的其中一个自增后没有来得及在内存中进行自加1另一个线程自增完后就直接往内存中那这个值了最后的结果肯定是不符合我们预期的。         故此由上图所示符合我们预期的效果就只有最前面的两个情况了但是这种情况也就是多线程串行化执行执行完 t1再执行t2代码如下所示 public class ThreadDemo4 {private static int count 0;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {for (int i 1; i 50000; i) {count;}});Thread t2 new Thread(() - {try {t1.join();} catch (InterruptedException e) {throw new RuntimeException(e);}for (int i 50000; i 100000; i) {count;}});t1.start();t2.start();t2.join();System.out.println(count: count);} }         结果如下                但是如此操作的话这个代码和多线程的运行就完全没有关系了                 2、代码二分析----内存可见性例子         代码如下 package thread;import java.util.Scanner;public class ThreadDemo22 {private static int flag 1;public static void main(String[] args) {Thread t1 new Thread(() - {while (flag 1) {System.out.println(这里是线程t1);}System.out.println(t1线程结束);});Thread t2 new Thread(() - {System.out.println(请输入flag的值);Scanner scanner new Scanner(System.in);flag scanner.nextInt();});t1.start();t2.start();} }         执行预期是当我们输入不等于1的值就打印 “t1线程结束”但是当我们输入结果为5最终执行结果却不是我们预期的效果执行结果如下                  flag值是1但是t1线程一直在循环运行虽然t2线程是在按照我们的要求改变flag的值为什么结果与预期是相悖的如此就涉及到了jvm内部的优化了和内存可见性相关         t1线程中的flag1这一操作有两个核心指令 1load读取内存中的flag值到寄存器 2拿着寄存器中的值和1进行比较条件跳转指令         这里load的每次操作取到的值都是一样的而当我们执行scanner操作修改flag的值时load这一指令已经执行上百亿次了且从内存中取数据这一操作是非常耗时的远远比条件跳转指令花时间这时由于load开销太大jvm就会产生怀疑怀疑这个load继续操作的必要性从而给出优化把从内存读数据load这一操作给优化掉了这样一来jvm就不会再从内存中拿数据而是把load拿到的值放到寄存器中从寄存器拿到数据进行比较。这样可以大幅度的提高循环的执行速度。         上面的例子t2修改了内存但是t1没看到内存变化就称为内存可见性问题。而内存可见性问题是高度依赖编译器优化的问题 3.2 线程不安全问题解决方法 3.2.1  t1 循环里加sleep 代码如下 public class ThreadDemo3 {private static int flag 1;public static void main(String[] args) {Thread t1 new Thread(() - {while (flag 1) {try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}//循环题里啥也不写}System.out.println(t1线程结束);});Thread t2 new Thread(() - {System.out.println(请输入flag的值);Scanner scanner new Scanner(System.in);flag scanner.nextInt();});t1.start();t2.start();} }         结果如下         方法详解因为10秒中都可以让t1线程里面的循环执行上百亿次cpu飞快的从内存中网寄存器中读取数据这样会导致load的开销就非常大代码的优化迫切程度就比较大但是加了sleep后我们让线程t1进行休眠如此load的开销就小了很多代码的优化迫切程度就降低了故此load就能将5这个值在优化前读取到寄存器和flag进行比较最终达到我们预期的效果 3.3.2 给flag变量加volatile修饰  代码如下 public class ThreadDemo3 {private volatile static int flag 1;public static void main(String[] args) {Thread t1 new Thread(() - {while (flag 1) {//循环题里啥也不写}System.out.println(t1线程结束);});Thread t2 new Thread(() - {System.out.println(请输入flag的值);Scanner scanner new Scanner(System.in);flag scanner.nextInt();});t1.start();t2.start();} }         结果与之前类似略         方法分析 1、java提供volatile关键字其核心作用就是保证 “内存可见性”另一功能禁止指令重排序可以使jvm强迫的优化强制关闭这样就可以确保循环每次都是从内存中拿数据了虽然这样执行效率也会下降但数据更为准确了 2、我们对编译器优化是这样表述的         编译器发现每次循环都要从内存中读取数据内存开销都太大了于是把读取内存这一操作优化成读取寄存器这一操作。         在JMM模型是这样描述的         编译器发现每次循环都要从 “主内存” 中读取数据就会把数据从 “主内存” 中复制下到 “工作内存” 中后续每次读取都是在 “工作内容” 这读取。这里的“工作内容代指cpu寄存器 缓存”----这里的 “主内存” 翻译成内存“工作内存” 翻译成cpu寄存器 3.3 线程不安全的原因 1、根本原因         操作系统上的线程是“抢占式执行”随机调度的----给线程之间的执行顺序带来了很多变数。 2、代码结构         多个线程同时修改同一个变量。     2.1、如果一个线程修改一个变量没事。     2.2、多个线程读取同一个变量没事的--如果只是读取变量的内容是固定不变的。     2.3、多个线程修改不同的变量没事---如果是两个不同的变量彼此之间就不会产生相互覆盖的情况了 3、直接原因         多线程修改操作本身不是原子的         即count该操作可以被细分为3个cpu指令,一个线程执行这些指令执行到一半会被调走从而给其他线程“可乘之机”------每个cpu指令都是原子的要么不执行要么执行完 4、内存可见性         一个线程读一个线程写也会导致线程安全的问题。 5、指令重排序         编译器的一种优化在保证代码逻辑不变的情况下将一些代码的指令重新排序从而提高代码的执行效率但是有时候会因为重排序后多线程编程就会出现线程安全问题。 番外         String是一个不可变对象 好处 方便jvm进行缓存放到字符串常量池中Hash值固定String的对象是线程安全的-à意味着只能读取不能修改 为什么说string是不可变的 持有的数据char 【】数组是private的里面没有提供public的方法来修改char数组的相关内容。Final只是表示不可被继承和可变没有关系。 3.4  针对上述原因给出的解决方案  针对原因1         我们无法给出解决方案因为操作系统内部已经实现了“抢占式执行”我们干预不了 针对原因2         分情况有的时候代码结构可以调整有的时候调整不了。 针对原因3         把要修改的变量这操作通过特殊手段把这操作在系统里的多个指令打包成一个“整体”。例如加锁操作而加锁的操作下一篇详细讲解就是把多个指令打包成一个原子的操作。 针对原因4         可以对代码进行调整避免内存可见性的问题也可以使用volatile进行修饰强制把代码优化关了这样数据就更准确了但执行效率也就变慢了。 针对原因5         将某些可能会指令重排序的变量加volatile修饰强制取消指令重排序的优化。 ps本次的内容就到这里了如果感兴趣的话就请一键三连哦
http://www.zqtcl.cn/news/128367/

相关文章:

  • 广州高档网站建设电子商务网站建设的期中考试
  • 九江建设公司网站新网 网站空间
  • 网站开发时的闭包写法手机网站创建站点成功
  • 中山做网站联系电话可以做全景的网站
  • 南京网站开发推南京乐识网络站点推广的方法有哪些
  • 沧州企业网站深圳建筑招聘网
  • 汽车网站开发的需求分析怎样策划一个营销型网站
  • 网站建设公司彩铃网站模板是怎么制作
  • 代做毕设网站推荐一键安装微信
  • 网站建设评比标准人工智能的网站
  • 网站 提示建设中计算机网站建设和维护
  • 网站菜单分类怎么做wordpress黄页插件
  • 安防网站下载营销型网站建设 高校邦
  • 一个几个人做网站的几个故事电影网站开发设计的完成情况
  • 如何开个人网站网站建设技能考试试题三
  • 做网站都要学什么工程造价询价网站
  • 东莞市官网网站建设企业福田做商城网站建设哪家服务周到
  • 网站界面设计技巧宁波seo排名优化价格
  • 做外贸经常用的网站需要优化的网站有哪些
  • 俄语网站建设注意事项seo公司优化排名
  • jsp做的当当网站的文档专业电子科技网站建设
  • 有免费的微网站是什么推广普通话调查问卷
  • 滁州市南谯区住房和建设局网站网站服务器规划 用户数
  • 静态企业网站源码网站sem托管
  • 17网站一起做网店打不开专业做网站公司 前景
  • 哪个网站可以做围棋作业游览有关小城镇建设的网站
  • 这么建立com的网站开发公司以现金方式补贴给客户
  • 网站建设 常见问题wordpress 手机顶部菜单
  • 医院网站 功能系统开发文档
  • 免费的企业网站网站空间商排名