实现全局自增id最简单有效的方式是什么?java.util.concurrent.atomic
包定义了一些常见类型的原子变量。这些原子变量为咱们提供了一种操做单一变量无锁(lock-free)的线程安全(thread-safe)方式。实际上该包下面的类为咱们提供了相似volatile
变量的特性,同时还提供了诸如boolean compareAndSet(expectedValue, updateValue)
的功能。不使用锁实现线程安全听起来彷佛很难以想象,这实际上是经过CPU的compare and swap指令实现的,因为硬件指令支持固然不须要加锁了。java
先不去讨论这些细节,咱们来看一下原子变量的用法。一个典型的用法是可使用原子变量轻松实现全局自增id,就像下面这样:git
// 线程安全的序列id生成器 class Sequencer { private final AtomicLong sequenceNumber = new AtomicLong(0); public long next() { return sequenceNumber.getAndIncrement(); } }
上述代码利用AtomicLong建立了一个Sequencer类,不断调用该类的next()方法就能够获得线程安全的自增id,用起来很是简单直观。下面咱们给出每种原子变量类型的用法说明。github
AtomicInteger和AtomicLong分别表明原子类型的整型和长整型,这两个类提供十分类似的功能,仅仅是位宽不一样。如上例所示,原子整型可用于多线程下全局自增id,除此以外还提供了原子比较-赋值等操做,诸如compareAndSet(expect, update)
, decrementAndGet()
,getAndDecrement()
,getAndSet(newValue)
等等,更全面的接口描述可参考JDK文档。须要提醒的是这些函数都是经过原子CPU指令实现,执行效率较高。数组
原子整型看似跟普通整型(Integer, Long)类型类似,但不能使用原子整型替代普通整型,由于原子整型是可变的,而普通整型不可变。因为这个缘由,使用原子整型做为Map的key并非个好主意。安全
你可能会想固然的觉得应该有AtomicFloat和AtomicDouble,遗憾的是类库里并无这两个类型,AtomicByte和AtomicShort也没有。若是须要替代方案是使用AtomicInteger和AtomicLong。可经过Float.floatToRawIntBits(float)
和Float.intBitsToFloat(int)
将Float存储到AtomicInteger中,相似的Double类型也能够存储到AtomicLong中。markdown
AtomicReference用于存放一个能够原子更新的对象引用。该类包含get()
, set()
, compareAndSet()
, getAndSet()
等原子方法来获取和更新其表明的对象引用。多线程
atomic包下面有三种原子数组:AtomicIntegerArray
, AtomicLongArra
, AtomicReferenceArray
,分别表明整型、长整型和引用类型的原子数组。原子数组使得咱们能够线程安全的方式去修改和访问数组里的单个元素。简单示例以下:函数
// 原子数组示例 AtomicLongArray longArray = new AtomicLongArray(10);// 建立长度为10的原子数组 longArray.set(1, 100); long v = longArray.getAndIncrement(1); AtomicReferenceArray<String> referenceArray = new AtomicReferenceArray<>(16); referenceArray.set(3, "love"); referenceArray.compareAndSet(3, "love", "you");
简单来讲原子数组就是一种支持线程安全的数组,仍然具备数组“定长”的性质,若是访问元素超过了数组的长度,将会抛出IndexOutOfBoundsException
。你可能已经想到了,可使用线程安全的容器来避免容量不足,咱们会在后续章节介绍。atom
线程安全是指多线程访问是时,不管线程的调度策略是什么,程序可以正确的执行。致使线程不安全的一个缘由是状态不一致,若是线程A修改了某个共享变量(好比给id++),而线程B没有及时知道,就会致使B在错误的状态上执行,结果的正确性也就没法保证。原子变量为咱们提供了一种保证单个状态一致的简单方式,一个线程修改了原子变量,另外的线程当即就能看到,这比经过锁实现的方式效率要高;若是要同时保证多个变量状态一致,就只能使用锁了。线程