一、原子性

原子行:即一个或者多个操作作为一个整体,要么全部执行,要么都不执行,并且操作在执行过程中不会被线程调度机制打断;而且这种操作一旦开始,就一直运行到结束,中间不会有任何上下文切换(context switch)。

我们用银行账户转账问题来形象的解释一下原子性(当然银行账户转账涉及到的问题比较多,我们这里只是来比拟一下)

举例一:
比如张三向李四转账200元,可以分解成如下步骤:
1)从张三账户减去200元
2)给李四账户加上200元
如果只执行步骤1),没有执行步骤2),问题就来了,张三说他给李四转钱了,李四说他没收到,银行该怎么处理这个事情呢?将该操作加上原子性就可以很好的解决转账问题。

举例二:
在java开发中我们经常使用如下语句

  1. int i = 0; //语句1
  2. i++; //语句2

语句1是一个原子性操作。

语句2的分解步骤是:
1)获取 i 的值;
2)计算 i + 1 的值;
3)将 i + 1 的值赋给 i;
执行以上3个步骤的时候是可以进行线程切换的,因此语句2不是一个原子性操作

二、可见性

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

举例:

  1. private int i = 0;
  2. private int j = 0;
  3. //线程1
  4. i = 10;
  5. //线程2
  6. j = i;

线程1修改i的值为10时的执行步骤:
1)将10赋给线程1工作内存中的 i 变量;
2)将线程1工作内存中的 i 变量的值赋给主内存中的 i 变量;

当线程2执行j = i时,线程2的执行步骤:
1)将主内存中的 i 变量的值读取到线程2的工作内存中;
2)将主内存中的 j 变量的值读取到线程2的工作内存中;
3)将线程2工作内存中的 i 变量的值赋给线程2工作内存中的 j 变量;
4)将线程2工作内存中的 j 变量的值赋给主内存中的 j 变量;

如果线程1执行完步骤1,线程2开始执行,此时主内存中 i 变量的值仍然为 0,那么线程2获取到的 i 变量的值为 0,而不是 10。

这就是可见性问题,线程1对 i 变量做了修改之后,线程2没有立即看到线程1修改的值。

三、有序性

有序性:即程序执行的顺序按照代码的先后顺序执行。

举例一:

  1. int i = 0;
  2. int j = 0;
  3. i = 10; //语句1
  4. j = 1; //语句2

语句可能的执行顺序如下:
1)语句1 语句2
2)语句2 语句1

语句1一定在语句2前面执行吗?答案是否定的,这里可能会发生执行重排(Instruction
Reorder)。一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序在单线程环境下最终执行结果和代码顺序执行的结果是一致的。

比如上面的代码中,语句1和语句2谁先执行对最终的程序结果并没有影响,那么就有可能在执行过程中,语句2先执行而语句1后执行。

举例二:

  1. int i = 0; //语句1
  2. int j = 0; //语句2
  3. i = i + 10; //语句3
  4. j = i * i; //语句4

语句可能的执行顺序如下:
1)语句1 语句2 语句3 语句4
2)语句2 语句1 语句3 语句4
3)语句1 语句3 语句2 语句4

语句3是不可能在语句4之后执行的,因为编译器在进行指令重排时会考虑数据的依赖性问题,语句4依赖于语句3,因此语句3一定在语句4之前执行。

接下来我们说一下多线程环境。

举例三:

  1. private boolean flag = false;
  2. private Context context = null;
  3. //线程1
  4. context = loadContext(); //语句1
  5. flag = true; //语句2
  6. //线程2
  7. while(!flag){
  8. Thread.sleep(1000L);
  9. }
  10. dowork(context);

语句可能的执行顺序如下:
1)语句1 语句2
2)语句2 语句1

由于在线程1中语句1、语句2是没有依赖性的,所以可能会出现指令重排。如果发生了指令重排,线程1先执行语句2,这时候线程2开始执行,此时flag值为true,因此线程2继续执行dowrk(context),此时context并没有初始化,因此就会导致程序错误。

因此可以得出结论,指令重排不会影响单线程的执行结果,但是会影响多线程并发执行的结果正确性。

总结:一个正确执行的并发程序,必须具备原子性、可见性、有序性。否则就有可能导致程序运行结果不正确,甚至引起死循环。

Java并发编程的3个特性的更多相关文章

  1. 转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

    简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...

  2. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  3. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

  4. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

  5. java并发编程实战学习(3)--基础构建模块

    转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...

  6. Java并发编程实现概览

    并发概览 >>同步 如何同步多个线程对共享资源的访问是多线程编程中最基本的问题之一.当多个线程并发访问共享数据时会出现数据处于计算中间状态或者不一致的问题,从而影响到程序的正确运行.我们通 ...

  7. 【多线程】Java并发编程:Lock(转载)

    原文链接:http://www.cnblogs.com/dolphin0520/p/3923167.html Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized ...

  8. Java并发编程:Lock

    Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.l ...

  9. java并发编程--Runnable Callable及Future

    1.Runnable Runnable是个接口,使用很简单: 1. 实现该接口并重写run方法 2. 利用该类的对象创建线程 3. 线程启动时就会自动调用该对象的run方法 通常在开发中结合Execu ...

随机推荐

  1. 移植OpenWrt到CuHead Pro WiFi

    移植OpenWrt到CuHead Pro WiFi Posted by: zou, baozhu , 三月 13, 2014 CuHead Pro是一款路由器开发板,下面是开发板的配置信息. 名称 型 ...

  2. ansible安装配置zabbix客户端

     安装软件 ansible host -m apt -a "name=zabbix-agent state=present" ansible host -m shell -a ...

  3. jQueryUI Sortable 应用Demo

    最近工作用需要设计一个自由布局的页面设计.我选了jQuery UI 的 sortable ,可以拖拽,自由排序 使用很方便,写一个demo,做个记录. 第一.单项目自由排序 下图效果 代码段: < ...

  4. 浅析bootstrap原理及优缺点

    网格系统的实现原理,是通过定义容器大小,平分12份(也有平分成24份或32份,但12份是最常见的),再调整内外边距,最后结合媒体查询,就制作出了强大的响应式网格系统   网格系统的实现原理,是通过定义 ...

  5. Storm集群详细部署

    1.安装zookeeper 3.1下载zookeeper安装包, 建议下载3.4.5及以上的版本 http://www.apache.org/dyn/closer.cgi/zookeeper/ 3.2 ...

  6. OGG for sqlserver engryption && insert/delete

    OGG for sqlserver engryption && insert/delete 1. 源端操作 1.1 获取key 作为数据库用户密码加密 d:\GoldenGate\gg ...

  7. Linux下zip格式文件的解压缩和压缩

    Linux下zip格式文件的解压缩和压缩 Linux下的软件包很多都是压缩包,软件的安装就是解压缩对应的压缩包.所以,就需要熟练使用常用的压缩命令和解压缩命令.最常用的压缩格式有.tar.gz/tgz ...

  8. Tornado之抽屉实战(3)--注册

    知识点应用:标签绑定事件,jQuery获取用户值-->AJAX发送数据-->后台路由系统-->业务逻辑处理-->ORM数据操作-->write返回-->AJAX回调 ...

  9. MongoDB简介及基础知识

    MongoDB简介 一.MongDB是一个高性能,开源,无模式的文档型NosQL数据库.主要功能特性: 1.文件存储格式BSON(一种json的扩展) 2.模式自由,数据格式不受限了表的结构 3.支持 ...

  10. Hadoop之MapReduce(一)简介及简单案例

    简介 Hadoop MapReduce是一个分布式运算编程框架,基于该框架能够容易地编写应用程序,进而处理海量数据的计算. MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算. ...