并发编程之原子性、可见性、有序性的简单理解

并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。

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

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

对于单线程,在执行代码时jvm会进行指令重排序,处理器为了提高效率,可以对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证保存最终执行结果和代码顺序执行的结果是一致的。

Java语言对原子性、可见性、有序性的保证

1、原子性

Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断,要么执行,要么不执行。

X=10;  //原子性(简单的读取、将数字赋值给变量)

Y = x;  //变量之间的相互赋值,不是原子操作

X++;  //对变量进行计算操作

X = x+1;

语句2实际包括两个操作,它先要去读取x的值,再将y值写入,两个操作分开是原子性的。合在一起就不是原子性的。

语句3、4:x++  x=x+1包括3个操作:读取x的值,x+1,将x写入

注:可以通过 synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。

2、可见性

Java提供了volatile关键字保证可见性。

当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值。

Synchronized和Lock也可以保证可见性,因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中,

3、有序性

在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述volatile关键字)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

 

Java内存模型:每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。

指令重排序:java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。

指令重排序的意义:JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。

下面就来具体介绍下happens-before原则(先行发生原则):

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作

锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作

volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作。

传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。

线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作

线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行

对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

时间: 2024-08-25 11:28:21

并发编程之原子性、可见性、有序性的简单理解的相关文章

并发编程之内存可见性

在上篇线程安全中,我们已经知道需要使用锁来同步管理对可变状态的访问操作.今天我们来看下并发编程的内存可见性问题. 同步代码块除了实现原子性或者临界区之外,其还保证了内存可见性,即保证其他线程可以看到状态的变化结果. private static boolean stop =false; private static int number = 0; public static class ReaderThread extends Thread { public void run() { while

Java并发编程与技术内幕:线程池深入理解

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要: 本文主要讲了Java当中的线程池的使用方法.注意事项及其实现源码实现原理,并辅以实例加以说明,对加深Java线程池的理解有很大的帮助. 首先,讲讲什么是线程池?照笔者的简单理解,其实就是一组线程实时处理休眠状态,等待唤醒执行.那么为什么要有线程池这个东西呢?可以从以下几个方面来考虑:其一.减少在创建和销毁线程上所花的时间以及系统资源的开销 .其二.2将当前任务与主线程隔离,能实现和主

JAVA并发编程补充-线程可见性

定义一个类Visibility1包含两个变量 public class Visibility1 { public static boolean ready; public static int number;}自定义一个线程类,当Visibility1中的ready属性为false时进入循环打印number public class ReaderThread extends Thread{ @Override public void run() { while (!Visibility1.rea

关于DOM脚本编程中不唐突性的简单理解

参考自:<JavaScript DOM 高级程序设计>第一章 遵循最佳实践 DOM脚本是为文档增强行为和交互性,当然文档需要符合W3C标准(有待进一步的学习).DOM脚本编程依赖于JavaScript,在这里就需要考虑不唐突性(unobtrusiveness)要强调的是,脚本必须是不唐突的,要消除一些不必要的行为和令人讨厌的功能.要保证在没有JavaScript的情况下,页面标记是持续有效的,这些当然会牺牲一些美观效果,是页面看起来不再那么优雅. 有关不唐突性的两个关键词“渐进增强(progr

并发编程

并发编程之内存可见性 并发编程之内存可见性 在上篇线程安全中,我们已经知道需要使用锁来同步管理对可变状态的访问操作.今天我们来看下并发编程的内存可见性问题. 同步代码块除了实现原子性或者临界区之外,其还保证了内存可见性,即保证其他线程可以看到状态的变化结果. private static boolean stop =false; private static int number = 0; public static class ReaderThread extends Thread { pub

并发编程的技巧和策略

可变状态是至关重要的. 所有的并发问题都可以归结为如何协调对并发状态的访问.可变状态越少,就越容易确保线程安全性. 尽量将域声明为final 类型,除非需要他们是可变的 不可变对象一定是线程安全的 不可变对象能极大地降低并发编程的复杂性.他们更为简单而且安全,可以任意共享而无需使用加锁或保护性复制等机制. 用锁来保护每个可变变量 当保护同一个不变性条件中的所有变量时,要使用同一个锁 在执行复合操作期间,要持有锁 如果从多个线程中访问同一个可变变量没有同步机制,那么程序就会出现问题 在设计过程中考

并发编程--AbstractQueuedSynchronizer介绍和原理分析

AbstractQueuedSynchronizer是并发编程包中最重要的类,是并发编程包的实现基层.简单来说,AbstractQueuedSynchronizer提供了一个基于FIFO的队列,用于存储处于阻塞状态的线程:提供了一个volatile修改的state变量,用于作为锁获取的标识位,对于FIFO队列和state的操作都是通过Unsafe这个类来实现CAS原子操作. AQS的功能可以分为两类:独占功能和共享功能,它的所有子类中,要么实现了并使用了它独占锁功能的API,要么使用了共享锁的功

并发编程之多线程

一.并发编程之多线程 1.线程简单介绍 进程是资源单位,把所有资源集中到一起,而线程是执行单位,真正执行的是线程 每个进程都有一个地址空间,而且默认就有一个控制线程 多线程:在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间.进程之间是竞争关系,线程之间是协作关系 线程的创建开销比进程小很多,运行较快 主线程从执行层面上代表了其所在进程的执行过程 2.线程开启方式 方式一:使用替换threading模块提供的Thread from threading import Thread  d

并发编程之学习总结

前言 楼主自1月14号就停更了,这段时间一直在看JVM,因此没有更新任何文章,但楼主觉得有必要总结一下之前学习并发的过程,因为这一次的总结其实就是下一次的开始.主要时总结一下并发的学习路线,因为在楼主的工作环境中,并不是每天都在编写高并发的程序.然而,即使是这样,我们也要知道并发的每个知识点,这样才算一个合格的Java 程序员.那么如何系统的学习并发呢?学习路线是怎么样的呢? 1. 学会开始就成功了一半 首先要确定的思想是:并发编程很复杂,不是看一两篇博客就搞定的.楼主学习学习并发主要看了4本书