volatile

ConcurrentHashMap原理

不想你离开。 提交于 2020-03-23 21:30:51
转载地址:http://www.blogjava.net/xylz/archive/2010/07/20/326661.html 在上一篇中介绍了HashMap的原理,这一节是ConcurrentMap的最后一节,所以会完整的介绍ConcurrentHashMap的实现。 ConcurrentHashMap原理 在 读写锁章节部分 介绍过一种是用读写锁实现Map的方法。此种方法看起来可以实现Map响应的功能,而且吞吐量也应该不错。但是通过前面对 读写锁原理 的分析后知道,读写锁的适合场景是读操作>>写操作,也就是读操作应该占据大部分操作,另外读写锁存在一个很严重的问题是读写操作不能同时发生。要想解决读写同时进行问题(至少不同元素的读写分离),那么就只能将锁拆分,不同的元素拥有不同的锁,这种技术就是“锁分离”技术。 默认情况下ConcurrentHashMap是用了16个类似HashMap 的结构,其中每一个HashMap拥有一个独占锁。也就是说最终的效果就是通过某种Hash算法,将任何一个元素均匀的映射到某个HashMap的Map.Entry上面,而对某个一个元素的操作就集中在其分布的HashMap上,与其它HashMap无关。这样就支持最多16个并发的写操作。 上图就是ConcurrentHashMap的类图。参考上面的说明和HashMap的原理分析

死磕 java集合之SynchronousQueue源码分析

隐身守侯 提交于 2020-03-23 16:23:15
问题 (1)SynchronousQueue的实现方式? (2)SynchronousQueue真的是无缓冲的吗? (3)SynchronousQueue在高并发情景下会有什么问题? 简介 SynchronousQueue是java并发包下无缓冲阻塞队列,它用来在两个线程之间移交元素,但是它有个很大的问题,你知道是什么吗?请看下面的分析。 源码分析 主要属性 // CPU的数量 static final int NCPUS = Runtime.getRuntime().availableProcessors(); // 有超时的情况自旋多少次,当CPU数量小于2的时候不自旋 static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32; // 没有超时的情况自旋多少次 static final int maxUntimedSpins = maxTimedSpins * 16; // 针对有超时的情况,自旋了多少次后,如果剩余时间大于1000纳秒就使用带时间的LockSupport.parkNanos()这个方法 static final long spinForTimeoutThreshold = 1000L; // 传输器,即两个线程交换元素使用的东西 private transient volatile Transferer<E>

ConcurrentHashMap 源码浅析 1.7

落花浮王杯 提交于 2020-03-23 15:38:18
简介 (1) 背景 HashMap死循环:HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry. HashTable效率低下:HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下.因为当一个线程访问HashTable的同步方法,其它线程也访问HashTable的同步方法时,会进入阻塞或轮询状态.如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法获取元素,所以竞争越激烈效率越低. (2) 简介 HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么多线程访问容器里不同的数据段时,线程间不会存在竞争,从而可以有效提高并发访问效率,这就是ConcurrentHash所使用的锁分段技术.首先将数据分成一段一段地储存,然后给每一段配一把锁,当一个线程占用锁访问其中一段数据时,其它段的数据也能被其它线程访问. 结构 ConcurrentHashMap是由Segments数组结构和HashEntry数组结构组成

归纳一下:C#线程同步的几种方法

房东的猫 提交于 2020-03-23 08:23:33
我们在编程的时候,有时会使用多线程来解决问题,比如你的程序需要在后台处 理一大堆数据,但还要使用户界面处于可操作状态;或者你的程序需要访问一些外部资源如数据库或网络文件等。这些情况你都可以创建一个子线程去处理,然而, 多线程不可避免地会带来一个问题,就是线程同步的问题。如果这个问题处理不好,我们就会得到一些非预期的结果。 在网上也看过一些关于线程同步的文章,其实线程同步有好几种方法,下面我就简单的做一下归纳。 一、volatile关键字 volatile是最简单的一种同步方法,当然简单是要付出代价的。它只能在变量一级做同步,volatile的含义就是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我。(【转自 www.bitsCN.com 】)因此,当多线程同时访问该变量时,都将直接操作主存,从本质上做到了变量共享。 能够被标识为volatile的必须是以下几种类型:(摘自MSDN) Any reference type. Any pointer type (in an unsafe context). The types sbyte, byte, short, ushort, int, uint, char, float, bool. An enum type with an enum base type of byte, sbyte, short, ushort, int

[原]Java多线程编程学习笔记之四:volatile变量修饰符—意料之外的问题(含代码)

僤鯓⒐⒋嵵緔 提交于 2020-03-23 06:20:50
转载请注明出处 大学生程序代写 > volatile用处说明 在JDK1.2之前,Java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的。 而随着JVM的成熟和优化,现在在多线程环境下volatile 关键字 的使用变得非常重要。 在当前的Java内存模型下, 线程 可以把 变量 保 存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个 变量 的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。 要解决这个问题,就需要把 变量 声明为volatile(不稳定的),这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下,各任务间共享的变量都应该加volatile修饰符。 Volatile修饰的 成员变量 在每次被 线程 访问时,都强迫从 共享内存 中重读该成员变量的值。而且,当 成员变量 发生变化时,强迫线程将变化值回写到 共享内存 。这样在任何时刻,两个不同的线程总是看到某个 成员变量 的同一个值。 Java语言 规范中指出:为了获得最佳速度,允许线程保存共享 成员变量 的私有拷贝,而且只当线程进入或者离开 同步代码块 时才将私有拷贝与共享内存中的原始值进行比较。 这样当多个线程同时与某个对象交互时,就必须注意到要让线程及时的得到共享 成员变量

【Java】高并发同步Volatile的使用

别等时光非礼了梦想. 提交于 2020-03-23 06:08:45
引言: 在多线程并发编程中synchronized和Volatile都扮演着重要的角色, Volatile 是 轻量级的Synchronized ,它在多处理器开发中保证了共享变量的“可见性”。 可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。 Volatile的官方定义 Java语言规范第三版中对volatile的定义如下: java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到这个变量的值是一致的。 为什么要使用Volatile? Volatile变量修饰符如果使用 恰当 的话,它比synchronized的 使用和执行成本会更低 ,因为它不会引起线程上下文的切换和调度。 Volatile的使用优化 著名的Java并发编程大师Doug lea在JDK7的并发包里新增一个队列集合类LinkedTransferQueue,他在使用Volatile变量时,用一种追加字节的方式来优化队列出队和入队的性能。 追加字节能优化性能?这种方式看起来很神奇,但如果深入理解处理器架构就能理解其中的奥秘。让我们先来看看LinkedTransferQueue这个类

Java多线程的volatile底层实现原理

瘦欲@ 提交于 2020-03-22 23:41:36
或许你经常被问到? Volatile关键字有何作用? 实现这些作用的底层如何实现? Volatile能够保障可见性、有序性?原子性吗? 前言 我们都知道,Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。 Volatile作用   Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。我们看下面的图来理解。2个CPU都要去操作主存中counter变量时,,他们读取主存中的变量counter到自己cpu cache中,然后操作数据。CPU1改变counter=7的操作,对CPU2是不可见的。 实现可见性 上面的例子,如果我们加上Volatile关键字,实际上底层是这么回事。 1)将当前处理器缓存行的数据写回到系统内存。 2)写回主存的操作会使在其他CPU里缓存了该内存地址的数据无效,其他CPU执行时,就需要重新从主存中获取数据。 实现禁止指令重排序 禁止指令重排序有没有什么例子?可以参考下我的另一篇文章: DCL的单例一定是线程安全的吗 Java内存模型其实是通过内存屏障(Memory Barrier)来实现的禁止指令重排序,

关于高并发的aotomic

我的梦境 提交于 2020-03-20 08:35:21
AtomicInteger线程安全的根源,熟悉并发的同学一定知道在java中处理并发主要有两种方式: 1,synchronized关键字,这个大家应当都各种面试和笔试中经常遇到。 2,volatile修饰符的使用,相信这个修饰符大家平时在项目中使用的也不是很多。 这里重点说一下volatile: Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存重新读取该成员的值,而且,当成员变量值发生变化时,强迫将变化的值重新写入共享内存,这样两个不同的线程在访问同一个共享变量的值时,始终看到的是同一个值。 java语言规范指出:为了获取最佳的运行速度,允许线程保留共享变量的副本,当这个线程进入或者离开同步代码块时,才与共享成员变量进行比对,如果有变化再更新共享成员变量。这样当多个线程同时访问一个共享变量时,可能会存在值不同步的现象。 而volatile这个值的作用就是告诉VM:对于这个成员变量不能保存它的副本,要直接与共享成员变量交互。 建议:当多个线程同时访问一个共享变量时,可以使用volatile,而当访问的变量已在synchronized代码块中时,不必使用。 缺点:使用volatile将使得VM优化失去作用,导致效率较低,所以要在必要的时候使用。 来源: https://www.cnblogs.com/mengziHEHE/p/3236313.html

原子操作类AtomicInteger详解

生来就可爱ヽ(ⅴ<●) 提交于 2020-03-20 08:08:13
为什么需要AtomicInteger原子操作类? 对于Java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的。num++解析为num=num+1,明显,这个操作不具备原子性,多线程并发共享这个变量时必然会出现问题。测试代码如下: public class AtomicIntegerTest { private static final int THREADS_CONUT = 20; public static int count = 0; public static void increase() { count++; } public static void main(String[] args) { Thread[] threads = new Thread[THREADS_CONUT]; for (int i = 0; i < THREADS_CONUT; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { increase(); } } }); threads[i].start(); } while (Thread.activeCount() > 1) {

原子操作类AtomicInteger详解

谁说我不能喝 提交于 2020-03-20 08:08:00
为什么需要AtomicInteger原子操作类? 对于Java中的运算操作,例如自增或自减,若没有进行额外的同步操作,在多线程环境下就是线程不安全的。num++解析为num=num+1,明显,这个操作不具备原子性,多线程并发共享这个变量时必然会出现问题。测试代码如下: public class AtomicIntegerTest { private static final int THREADS_CONUT = 20; public static int count = 0; public static void increase() { count++; } public static void main(String[] args) { Thread[] threads = new Thread[THREADS_CONUT]; for (int i = 0; i < THREADS_CONUT; i++) { threads[i] = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { increase(); } } }); threads[i].start(); } while (Thread.activeCount() > 1) {