volatile

ConcurrentHashMap源码分析

隐身守侯 提交于 2020-02-22 11:48:17
ConcurrentHashMap解决了HashMap的线程不安全问题,在分析之前先介绍一个将HashMap线程安全的方法。利用 Collections.synchronizedMAp方法 调用内部类 SynchronizedMap HashMap<String, String> map = new HashMap<>(); Map m = Collections.synchronizedMap(map); private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { this.m = Objects.requireNonNull(m); mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; this.mutex =

线程安全性-可见性

a 夏天 提交于 2020-02-22 05:05:26
导致共享变量在线程间不可见的原因 线程交叉执行;重排序结合线程交叉执行;共享变量更新后的值没有在工作内存与主存间及时更新 JMM关于synchronized的两条规定 线程解锁前,必须把共享变量的最新值刷新到主内存;线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主存中重新读取最新的值(注意加锁和解锁是同一把锁) 可见性-volatile 通过加入 内存屏障 和 禁止重排序 优化来实现; 对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量刷到之内存; 对volatile变量读操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量。 可见性-volatile读 可见性-volatile读 可见性-volatile使用 当变量作为多个线程之间的交流变量 valatile boolean inited = false ; //线程1 context = loadContext ( ) ; inited = true ; //线程2 while ( ! inited ) { sleep ( ) ; } doSomethingWithConfig ( context ) ; 这段代码中,线程1的context=loadContext()和inited=true的操作不一定会按着代码逻辑顺序来的

Volatile

戏子无情 提交于 2020-02-21 23:44:29
什么是volatile 1.Java语言规范第3版中对volatile的定义如下:   Java编程语言允许线程访问共享变量,为了确保共享变量能够被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁要更加方便。如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值一致。 2.通俗理解:   volatile就是Java的一个关键字,单词volatile本身具有不稳定的意思。volatile关键字表示被修饰的变量的值容易变化,不稳定。volatile变量的不稳定性意味着对这种变量的读和写操作都必须从高速缓存或者主内存中读取,以读取变量相对新的值。 volatile的作用 1.保障读操作、写操作本身的原子性 1. 原理:volatile关键字在原子性方面仅保障对被修饰的变量的读操作、写操作本身的原子性,如果要保障对volatile变量的赋值操作的原子性,那么这个赋值操作不能涉及任何共享变量(包括被赋值的volatile变量本身)的访问。 例子1:num1=num2+1; 如果变量num2也是一个共享变量,那么赋值操作实际上是一个read-modify-write操作。其执行过程中其他线程可能已经更新了num2的值,因此该操作不具备不可分割性,也就不是原子操作。如果变量num2是一个局部变量

volatile怎么保证可见性,synchronized和lock的区别,synchronized的底层实现

无人久伴 提交于 2020-02-21 05:48:05
可见性是指:当多个线程访问同一个变量时,某个线程修改了该变量的值,其他线程可以立即看到修改后的变量。当一个共享变量被volatile修饰后,一旦该变量的值被修改,修改后的变量会被理机更新到主存中,当有其他线程需要该变量的值会到主存中读取。 (存在层次)synchronized是JVM实现的,他是java的关键字。lock是java中的接口。 (锁的释放)当出现异常的时候,synchronized会主动释放线程占用的锁,因此不会出现死锁现象。但是lock需要通过unlock( )主动释放占用的锁,因此可能会出现死锁。我们需要在final部分释放占用的锁。 (锁的获取)使用synchronized时,等待锁的线程会一直等待下去不会响应中断,但是使用Lock的线程可以响应中断。 (锁的状态)synchronized无法判断线程是否已经获取了锁;但是Lock可以。 (锁的类型)synchronized的锁是可重入、不可中断、非公平的;Lock是可重入、可中断、可公平的。 可重入指执行对象中的同步方法不用再次获得锁。公平锁指按要获取锁的线程的等待时间分配锁,等待时间越长越有获取锁的优先权。 (锁的性能)Lock可以通过readwriteblock进行读写分离,提升线程的读写效率。当资源竞争不激烈的情形下,synchronized的性能优于Lock,但是当资源竞争激烈时

volitale 关键字

余生长醉 提交于 2020-02-21 04:16:41
一、前言 volitale 知识点主要涉及JAVA内存模型的知识点,volitale提供了内存可见性和禁止指令重排的功能。 参考文章: https://blog.csdn.net/eff666/article/details/67640648 二、知识点 1.内存可见性 对变量进行访问和赋值时,观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令。lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: (1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成; (2)它会强制将对缓存的修改操作立即写入主内存; (3)如果是写操作,它会导致其他CPU中对应的缓存行无效。 处理器为了提高处理速度,不直接和内存进行通讯,而是将系统内存的数据独到内部缓存后再进行操作,但操作完后不知什么时候会写到内存。 如果对声明了volatile变量进行写操作时,JVM会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写会到系统内存。这一步确保了如果有其他线程对声明了volatile变量进行修改,则立即更新主内存中数据。 但这时候其他处理器的缓存还是旧的

安全并发之先行发生原则

蓝咒 提交于 2020-02-21 03:47:32
java天生就是多线程的语言,线程安全则是一个必须的要求。 给你一段代码,你如何判定其是否是线程安全的?那么,自然就要有一套绝对的理论给你的证明提供依据了。   先行发生原则,可以帮你判定是否并发安全的,从而不必去猜测是否是线程安全了!   如果Java内存模型中所有有序性都靠volatile和synchronized来完成,那么编写代码会很繁琐,但日常Java开发中并没有感受到这一点,正是因为Java语言的“先行发生”原则。这个原则非常重要,它是判断数据是否存在竞争、线程是否安全的主要依据。   先行发生是Java内存模型中定义的两项操作数之间的偏序关系,如果说操作A先行发生于操作B,就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。   下面是Java内存模型中一些“天然的”先行发生关系,这些先行发生关系无需任何同步协助器协作即可存在,可以直接在编码中使用。如果两个关系不在此列,而又无法通过这些关系推导出来,它们的顺序就无法保证,虚拟机可以对它们任意重排序。    程序次序规则: 同一个线程内,按照代码出现的顺序,前面的代码 happens-before 后面的代码,准确的说是控制流顺序,因为要考虑到分支和循环结构。    管程锁定规则: 对于一个监视器锁的unLock操作 happens-before

关于多线程面试题

流过昼夜 提交于 2020-02-20 04:39:20
1. 关键字volatile和synchronized对比? 1)volatile是线程同步的轻量级实现,而synchronized是重量级操作。volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。 2)volatile仅能保证数据可见性,不能保证原子性,而synchronized既可以保证数据的可见性,也能保证原子性。 3)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。 4)volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。 2. Lock和synchronized对比? 1)lock是一个锁,而synchronized是一个关键字。 2)lock不会主动释放锁,需要在finally块中添加unlock()去释放锁。而synchronized会主动释放锁。 3)lock可以获取锁的信息,例如是否成功获取锁,而synchronized不能。 4)lock可以让等待的线程响应中断,而synchronized不能。 5)lock可以提高多个线程读操作的效率,而synchronized不能。 3. sleep方法和wait方法的比较 相同点: 二者都可以让线程处于冻结状态。 不同点: 1)sleep方法是Thread类中定义的方法

Java中Volatile关键字详解

﹥>﹥吖頭↗ 提交于 2020-02-19 02:24:47
这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问。 内存的可见性 确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。 上面的程序中NoVisibility可能会持续循环下去,因为读线程可能永远都看不到ready的值。一种更奇怪的现象是 NoVisibility可能会输出0,因为读线程可能看到了写入ready的值,但却没有看到之后写入number的值,这种现象被称为“ 重排序 ”。 多线程之指令重排序 失效数据 简而言之就是在缺乏同步的程序中可能会读取到过期的数据,也就是失效数据,就像上面的例子一样,当度线程查看ready变量时可能会的得到一个失效的值。 非原子的64位操作 虽然得到的可能是一个失效值,但至少这个值是由之前每个线程设置的,而不是一个随机值。这种安全性保证也被称为 最低安全性 。最低安全性适用于绝大多数变量,但是对于非volatile类型的64位数值变量(double和long)并非如此。java变量的读操作和写操作都是原子的,但是JVM允许将64位的读操作和写操作分解为2个32为的操作。这样当读取一个非volatile的long变量时,如果对该变量的读操作和写操作在不同的线程中执行,那么很可能会读取到某个值的高32为和另一个值的低32位。 加锁与可见性 Volatile变量 确保将变量的更新操作通知到其他线程

synchronized和volatile的区别

你说的曾经没有我的故事 提交于 2020-02-18 22:21:37
一、控制区别    首先我们要了解一下是什么是控制层面的区别,这里提到两点:   1.执行控制 目的是 控制代码执行(顺序) 及是否可以并发执行。   2.内存可见控制 线程执行结果在 内存中对其它线程的可见性 。根据Java内存模型的实现,线程在具体执行时,会先拷贝主存数据到线程本地(CPU缓存), 操作完成后再把结果从线程本地刷到主存 二、synchronized(执行控制)   1.synchronized会阻止其他线程获取当前的锁。   2.并且 synchronized 会将数据直接刷到主存,保证数据的可见性,同时也使得这个锁线程的所有操作都happends-before于之后获得这个锁的线程的操作。 三、volatile(内存可见性控制)   1.直接将数据刷到主存,保证内存可见性。   2.禁止指令重排 ("为了提高性能,编译器和处理器会对指令做重排序")   3.具有可见性、有序性,不具备原子性。   注“ 内存屏障指令保证了所有CPU操作结果都会直接刷到主存中。 将刷出所有在Barrier之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。 四、两者之间的区别   1.volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量

CopyOnWriteArrayList 源码解析和设计思路

百般思念 提交于 2020-02-18 20:04:21
引导语 在 ArrayList 的类注释上,JDK 就提醒了我们,如果要把 ArrayList 作为共享变量的话,是线程不安全的,推荐我们自己加锁或者使用 Collections.synchronizedList 方法,其实 JDK 还提供了另外一种线程安全的 List,叫做 CopyOnWriteArrayList,这个 List 具有以下特征: 线程安全的,多线程环境下可以直接使用,无需加锁; 通过锁 + 数组拷贝 + volatile 关键字保证了线程安全; 每次数组操作,都会把数组拷贝一份出来,在新数组上进行操作,操作成功之后再赋值回去。 1 整体架构 从整体架构上来说,CopyOnWriteArrayList 数据结构和 ArrayList 是一致的,底层是个数组,只不过 CopyOnWriteArrayList 在对数组进行操作的时候,基本会分四步走: 加锁; 从原数组中拷贝出新数组; 在新数组上进行操作,并把新数组赋值给数组容器; 解锁。 除了加锁之外,CopyOnWriteArrayList 的底层数组还被 volatile 关键字修饰,意思是一旦数组被修改,其它线程立马能够感知到,代码如下: private transient volatile Object[] array; 整体上来说,CopyOnWriteArrayList 就是利用锁 + 数组拷贝 +