范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Java并发编程核心理论

  并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能。它要求编程者对计算机最底层的运作原理有深刻的理解,同时要求编程者逻辑清晰、思维缜密,这样才能写出高效、安全、可靠的多线程并发程序。本系列会从线程间协调的方式(wait、notify、notifyAll)、Synchronized及Volatile的本质入手,详细解释JDK为我们提供的每种并发工具和底层实现机制。在此基础上,我们会进一步分析java.util.concurrent包的工具类,包括其使用方式、实现源码及其背后的原理。本文是该系列的第一篇文章,是这系列中最核心的理论部分,之后的文章都会以此为基础来分析和解释。
  一、共享性
  数据共享性是线程安全的主要原因之一。如果所有的数据只是在线程内有效,那就不存在线程安全性问题,这也是我们在编程的时候经常不需要考虑线程安全的主要原因之一。但是,在多线程编程中,数据共享是不可避免的。最典型的场景是数据库中的数据,为了保证数据的一致性,我们通常需要共享同一个数据库中数据,即使是在主从的情况下,访问的也同一份数据,主从只是为了访问的效率和数据安全,而对同一份数据做的副本。我们现在,通过一个简单的示例来演示多线程下共享数据导致的问题:
  代码段一:
  package com.paddx.test.concurrent;
  public class ShareData {
  public static int count = 0 ;
  public static void main(String[] args) {
  final ShareData data = new ShareData();
  for ( int i = 0 ; i < 10 ; i++) {
  new Thread( new Runnable() {
  @Override
  public void run() {
  try {
  //进入的时候暂停1毫秒,增加并发问题出现的几率
  Thread.sleep( 1 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  for ( int j = 0 ; j < 100 ; j++) {
  data.addCount();
  }
  System.out.print(count + " " );
  }
  }).start();
  }
  try {
  //主程序暂停3秒,以保证上面的程序执行完成
  Thread.sleep( 3000 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  System.out.println( "count=" + count);
  }
  public void addCount() {
  count++;
  }
  }
  上述代码的目的是对count进行加一操作,执行1000次,不过这里是通过10个线程来实现的,每个线程执行100次,正常情况下,应该输出1000。不过,如果你运行上面的程序,你会发现结果却不是这样。下面是某次的执行结果(每次运行的结果不一定相同,有时候也可能获取到正确的结果):
  可以看出,对共享变量操作,在多线程环境下很容易出现各种意想不到的的结果。
  二、互斥性
  资源互斥是指同时只允许一个访问者对其进行访问,具有唯一性和排它性。我们通常允许多个线程同时对数据进行读操作,但同一时间内只允许一个线程对数据进行写操作。所以我们通常将锁分为共享锁和排它锁,也叫做读锁和写锁。如果资源不具有互斥性,即使是共享资源,我们也不需要担心线程安全。例如,对于不可变的数据共享,所有线程都只能对其进行读操作,所以不用考虑线程安全问题。但是对共享数据的写操作,一般就需要保证互斥性,上述例子中就是因为没有保证互斥性才导致数据的修改产生问题。Java 中提供多种机制来保证互斥性,最简单的方式是使用Synchronized。现在我们在上面程序中加上Synchronized再执行:
  代码段二:
  package com.paddx.test.concurrent;
  public class ShareData {
  public static int count = 0 ;
  public static void main(String[] args) {
  final ShareData data = new ShareData();
  for ( int i = 0 ; i < 10 ; i++) {
  new Thread( new Runnable() {
  @Override
  public void run() {
  try {
  //进入的时候暂停1毫秒,增加并发问题出现的几率
  Thread.sleep( 1 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  for ( int j = 0 ; j < 100 ; j++) {
  data.addCount();
  }
  System.out.print(count + " " );
  }
  }).start();
  }
  try {
  //主程序暂停3秒,以保证上面的程序执行完成
  Thread.sleep( 3000 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  System.out.println( "count=" + count);
  }
  /**
  * 增加 synchronized 关键字
  */
  public synchronized void addCount() {
  count++;
  }
  }
  现在再执行上述代码,会发现无论执行多少次,返回的最终结果都是1000。
  三、原子性
  原子性就是指对数据的操作是一个独立的、不可分割的整体。换句话说,就是一次操作,是一个连续不可中断的过程,数据不会执行的一半的时候被其他线程所修改。保证原子性的最简单方式是操作系统指令,就是说如果一次操作对应一条操作系统指令,这样肯定可以能保证原子性。但是很多操作不能通过一条指令就完成。例如,对long类型的运算,很多系统就需要分成多条指令分别对高位和低位进行操作才能完成。还比如,我们经常使用的整数 i++ 的操作,其实需要分成三个步骤:(1)读取整数 i 的值;(2)对 i 进行加一操作;(3)将结果写回内存。这个过程在多线程下就可能出现如下现象:
  这也是代码段一执行的结果为什么不正确的原因。对于这种组合操作,要保证原子性,最常见的方式是加锁,如Java中的Synchronized或Lock都可以实现,代码段二就是通过Synchronized实现的。除了锁以外,还有一种方式就是CAS(Compare And Swap),即修改数据之前先比较与之前读取到的值是否一致,如果一致,则进行修改,如果不一致则重新执行,这也是乐观锁的实现原理。不过CAS在某些场景下不一定有效,比如另一线程先修改了某个值,然后再改回原来值,这种情况下,CAS是无法判断的。
  四、可见性
  要理解可见性,需要先对JVM的内存模型有一定的了解,JVM的内存模型与操作系统类似,如图所示:
  从这个图中我们可以看出,每个线程都有一个自己的工作内存(相当于CPU高级缓冲区,这么做的目的还是在于进一步缩小存储系统与CPU之间速度的差异,提高性能),对于共享变量,线程每次读取的是工作内存***享变量的副本,写入的时候也直接修改工作内存中副本的值,然后在某个时间点上再将工作内存与主内存中的值进行同步。这样导致的问题是,如果线程1对某个变量进行了修改,线程2却有可能看不到线程1对共享变量所做的修改。通过下面这段程序我们可以演示一下不可见的问题:
  package com.paddx.test.concurrent;
  public class VisibilityTest {
  private static boolean ready;
  private static int number;
  private static class ReaderThread extends Thread {
  public void run() {
  try {
  Thread.sleep( 10 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  if (!ready) {
  System.out.println(ready);
  }
  System.out.println(number);
  }
  }
  private static class WriterThread extends Thread {
  public void run() {
  try {
  Thread.sleep( 10 );
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  number = 100 ;
  ready = true ;
  }
  }
  public static void main(String[] args) {
  new WriterThread().start();
  new ReaderThread().start();
  }
  }
  从直观上理解,这段程序应该只会输出100,ready的值是不会打印出来的。实际上,如果多次执行上面代码的话,可能会出现多种不同的结果,下面是我运行出来的某两次的结果:
  当然,这个结果也只能说是有可能是可见性造成的,当写线程(WriterThread)设置ready=true后,读线程(ReaderThread)看不到修改后的结果,所以会打印false,对于第二个结果,也就是执行if (!ready)时还没有读取到写线程的结果,但执行System.out.println(ready)时读取到了写线程执行的结果。不过,这个结果也有可能是线程的交替执行所造成的。Java 中可通过Synchronized或Volatile来保证可见性,具体细节会在后续的文章中分析。
  五、有序性
  为了提高性能,编译器和处理器可能会对指令做重排序。重排序可以分为三种:
  (1)编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  (2)指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  (3)内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
  我们可以直接参考一下JSR 133 中对重排序问题的描述:
  (1)                    (2)
  先看上图中的(1)源码部分,从源码来看,要么指令 1 先执行要么指令 3先执行。如果指令 1 先执行,r2不应该能看到指令 4 中写入的值。如果指令 3 先执行,r1不应该能看到指令 2 写的值。但是运行结果却可能出现r2==2,r1==1的情况,这就是"重排序"导致的结果。上图(2)即是一种可能出现的合法的编译结果,编译后,指令1和指令2的顺序可能就互换了。因此,才会出现r2==2,r1==1的结果。Java 中也可通过Synchronized或Volatile来保证顺序性。
  六 总结
  本文对Java 并发编程中的理论基础进行了讲解,有些东西在后续的分析中还会做更详细的讨论,如可见性、顺序性等。后续的文章都会以本章内容作为理论基础来讨论。如果大家能够很好的理解上述内容,相信无论是去理解其他并发编程的文章还是在平时的并发编程的工作中,都能够对大家有很好的帮助。

纪念诗人胡续冬他的一首诗,让我想起一个故事2021年8月22日下午,诗人北京大学外国语学院副教授胡续冬(1974年10月2021年8月)在北京去世,终年47岁。胡续冬,原名胡旭东,北京大学外国语学院世界文学研究所副教授北京你抖了吗?对李叫兽历史数据如何扭曲了你的判断的补充你抖了吗?对李叫兽历史数据如何扭曲了你的判断的补充抖抖的一笔虎,有些书法家单爱写这个今天开始从头学习李叫兽,从公众号的第一篇学起。李叫兽在历史数据如何扭曲了你的判断一文的最后写道如诗什么时候会被写完?我在知乎的回答。1234567,乐曲会被做完吗?黑白,围棋会被下完吗?请看下去,我是精于回答这个问题,不是杠精。1按鲁迅的说法,不是到什么时候,是1000年前就已经写完了。我以为一No作更high这一次,不作诗的白居易,更精彩诗人也不一定要留诗,讲个白居易的故事吧。我们知道,李白路过黄鹤楼的时候,曾搁笔叹息而去,以致后人留下眼前有景道不得,崔颢题诗在上头的附会。这个大家都熟悉,就不细说了。白居易不留诗的学生被学校强制要求到工厂实习致残,责任该如何判定?图片来自网络,侵删。知乎上有人问学生被学校强制要求到工厂实习致残,而学校让自行与工厂协商,学生在实习期间受伤,责任该如何判定?下面是我的回答。2月19日,小鹏右手被卷入机器,出事时自动洗碗机的工作原理1自动洗碗机全自动洗碗机通过高温高压水流对餐具表面进行360机械清洗,洗涤剂和热水将餐具表面的油污和残渣分开,达到清洗和清洁两大功能。1。机械作用(1)在洗涤泵的驱动下,洗涤剂直接酒店应该如何选择洗碗机?如果不雇佣太多工人,但也需要让酒店老板主更放心,假期洗碗工离开不用担心,可以像一个服务员可以洗碗,清洁程度也很好,为何不选择洗碗机?酒店应该如何选择洗碗机?1,品牌购买。工厂技术要全自动洗碗机的几个优点全自动洗碗机是一种集排渣清洗烘干杀菌于一体的洗碗机。自动洗碗机有几个优点。1高效率。自动洗碗机可洗40005000碗。换言之,洗碗机一个小时可以相做相当于洗碗工几个小时的的工作。2商用洗碗机与人工洗碗的比较对于长期在厨房洗碗的人来说,洗碗机无疑是让他们开心的武器。如今,洗碗机不仅具有洗碗的功能,还具有烘干和消毒的功能。洗碗机采用高压水喷淋清洗方式,有智能清洗可洗超快清洗节能四种清洗方使用商用洗碗机后如何保持餐具清洁?在新冠状病毒到来的特殊时期,全体餐饮人员要提高清洁消毒意识,并将其投入日常工作,最大限度地减少损失,减少恐慌。使用商用洗碗机后如何正确清洁餐具?今天,我要带你去了解更多!1。洗碗机用洗碗机洗碗的优势你有没有觉得用手洗盘子太慢了吗?每次洗完餐具还需要换消毒柜消毒吗?如果你是老板,你在招聘洗碗工时肯定会遇到困难,然后你就需要大量的洗碗工来提供洗碗服务。再者,如果不注意员工的闲散,
三星夺全球5GAndroid智能手机14市场占有率,A52s最畅销韩国三星电子夺下全球5GAndroid智能手机近四分之一销售市场占有率,其中GalaxyA52s5G为全球最畅销的5GAndroid手机。调查公司Counterpoint26日公布IDC一季度新能源车市场L2级自动驾驶新车渗透率达354月27日,资本邦了解到,近日,IDC发布中国自动驾驶乘用车市场数据追踪报告。报告显示,2022年第一季度,国内L2级自动驾驶在乘用车市场的新车渗透率高达23。2。其中,新能源车市工信部国内互联网虚拟专用网业务IPVPN许可证牌照申请材料及条件申请国内互联网虚拟专用网业务IPVPN许可证电信业务分基础电信业务和增值电信业务。基础电信业务指提供公共网络基础设施公共数据传送和基本语音通信服务的业务。增值电信业务指利用公共网络余承东官宣好消息,华为手机供应链稳了,以后想买就能买到华为折叠屏新机MateXs2将于4月28日发布,华为高管余承东这段时间一直在为MateXs2预热,他表示,MateXs2接近普通手机的厚度和重量,解决了折叠屏手机笨重不耐摔的问题,1500亿市场保卫战,华为入库,联想坐不住了?目前,有很多设备其实都有商用和私用这样的区分,比较常见的应该就是PC了,在商用终端领域中,联想无疑是其中王者一般的存在。这些年来,联想也凭借着商用终端市场,赚得盆满钵满。但是,随着谎称特斯拉刹车失灵被打脸,EDR数据显示车主持续大幅踩下油门日前引发热议的特斯拉在北京机场二高速上刹车失灵加速到170一事,终于在车辆EDR数据提取后有了定论。据新浪汽车报道,4月27日,特斯拉与当事车主在第三方鉴定专家和国家汽车质量监管相特斯拉回应北京机场高速追尾事故车主误踩加速踏板来源中国新闻网中新财经4月27日电(葛成)针对4月22日发生在北京机场二高速的追尾事故,27日,特斯拉方面向中新财经回应称,数据显示,该事故系车主误操作造成。此前,当事人曾在社交媒刘强东案重启,涉案女孩是北京富商独女?149页警方档案引争议这两天,刘强东明州女大学生案重启调查冲上了热搜!沉寂3年多的事件,再次进入了公众视野。01hr原本,这次听证会将在美国当地时间4月25日下午(北京时间4月26日清晨46点)进行,也火热的新能源车市,透明的北京现代门店内无车,消费者无感,经销商无奈每经记者李硕每经编辑裴健如新能源汽车销量持续高涨之际,北京现代旗下多个新能源车型却处于停产边缘。(我们)店里目前没有(新能源车),菲斯塔和昂希诺纯电都已经停产了。名图纯电应该还有一苹果也上架了智能水壶,替代妈妈姥姥们,提醒监控你日常喝水近日苹果官网上又悄无声息的上架了2款智能水壶(我们中国叫保温杯),一款名叫HidrateSparkPROSTEELSmartWaterBottle另一款为HidrateSparkP为什么大厂控成本都选择裁员,而不是降薪?近日各大厂纷纷传出裁员风波,也有不少小伙伴问我,既然企业选择控制成本,为什么是裁员不是降薪?裁员和降薪唯一的共同点就是节约企业成本。一利益最大化互联网公司一般都选择裁员,传统制造业