实现全局自增id最简单有效的方式是什么?java.util.concurrent.atomic包定义了一些常见类型的原子变量。这些原子变量为我们提供了一种操作单一变量无锁(lock-free)的线程安全(thread-safe)方式。实际上该包下面的类为我们提供了类似volatile变量的特性,同时还提供了诸如boolean compareAndSet(expectedValue, updateValue)的功能。不使用锁实现线程安全听起来似乎很不可思议,这其实是通过CPU的compare and swap指令实现的,由于硬件指令支持当然不需要加锁了。

先不去讨论这些细节,我们来看一下原子变量的用法。一个典型的用法是可以使用原子变量轻松实现全局自增id,就像下面这样:

// 线程安全的序列id生成器
class Sequencer {
private final AtomicLong sequenceNumber = new AtomicLong(0);
public long next() {
return sequenceNumber.getAndIncrement();
}
}

上述代码利用AtomicLong创建了一个Sequencer类,不断调用该类的next()方法就可以得到线程安全的自增id,用起来非常简单直观。下面我们给出每种原子变量类型的用法说明。

AtomicInteger and AtomicLong

AtomicIntegerAtomicLong分别代表原子类型的整型和长整型,这两个类提供十分相似的功能,仅仅是位宽不同。如上例所示,原子整型可用于多线程下全局自增id,除此之外还提供了原子比较-赋值等操作,诸如compareAndSet(expect, update)decrementAndGet()getAndDecrement()getAndSet(newValue)等等,更全面的接口描述可参考JDK文档。需要提醒的是这些函数都是通过原子CPU指令实现,执行效率较高。

原子整型看似跟普通整型(Integer, Long)类型相似,但不能使用原子整型替代普通整型,因为原子整型是可变的,而普通整型不可变。由于这个原因,使用原子整型作为Map的key并不是个好主意。

你可能会想当然的以为应该有AtomicFloatAtomicDouble,遗憾的是类库里并没有这两个类型,AtomicByteAtomicShort也没有。如果需要替代方案是使用AtomicIntegerAtomicLong。可通过Float.floatToRawIntBits(float)Float.intBitsToFloat(int)将Float存储到AtomicInteger中,类似的Double类型也可以存储到AtomicLong中。

AtomicReference

AtomicReference用于存放一个可以原子更新的对象引用。该类包含get(), set(), compareAndSet(), getAndSet()等原子方法来获取和更新其代表的对象引用。

AtomicXXXArray

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。你可能已经想到了,可以使用线程安全的容器来避免容量不足,我们会在后续章节介绍。

什么是线程安全?

线程安全是指多线程访问是时,无论线程的调度策略是什么,程序能够正确的执行。导致线程不安全的一个原因是状态不一致,如果线程A修改了某个共享变量(比如给id++),而线程B没有及时知道,就会导致B在错误的状态上执行,结果的正确性也就无法保证。原子变量为我们提供了一种保证单个状态一致的简单方式,一个线程修改了原子变量,另外的线程立即就能看到,这比通过锁实现的方式效率要高;如果要同时保证多个变量状态一致,就只能使用锁了。

本文地址

Java原子变量的更多相关文章

  1. 谈论Java原子变量和同步的效率 -- 颠覆你的生活

    我们认为,由于思维定式原子变量总是比同步运行的速度更快,我想是这样也已经,直到实现了ID在第一次测试过程生成器不具有在这样一个迷迷糊糊的东西. 测试代码: import java.util.Array ...

  2. Java原子变量类需要注意的问题

    在学习多线程时,遇到了原子变量类,它是基于 CAS 和 volatile 实现的,能够保障对共享变量进行 read-modify-write 更新操作的原子性和可见性.于是我就写了一段代码试试,自认为 ...

  3. 全面了解 Java 原子变量类

    目录   一.原子变量类简介  二.基本类型  三.引用类型  四.数组类型  五.属性更新器类型  参考资料

  4. 用Java原子变量的CAS方法实现一个自旋锁

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/5999610. ...

  5. Java并发之原子变量和原子引用与volatile

    我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步.同步的包含两层作用:1)互斥访问(原子性):2)可见性:也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线 ...

  6. java并发编程学习: 原子变量(CAS)

    先上一段代码: package test; public class Program { public static int i = 0; private static class Next exte ...

  7. 《Java并发编程实战》第十五章 原子变量与非堵塞同步机制 读书笔记

    一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能 ...

  8. Java并发编程之原子变量

    原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量的原子操作.只是synchronized的成本相对较高,需要获取锁对象,释放锁对象,如果不能获取到锁,还 ...

  9. Java多线程-----原子变量和CAS算法

       原子变量      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题      Java给我们提供了以下几种原子类型: AtomicInteger和Ato ...

随机推荐

  1. Java设计模式之《桥接模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6497919.html 这里摘抄一份他处的概念,你可以不必理会,先看下面得讲解与实例, ...

  2. MySQL索引的使用方式

    一,create CREATE INDEX可对表增加普通索引或UNIQUE索引. CREATE INDEX index_name ON table_name (column_list) CREATE ...

  3. MAVEN学习(初级)

    1. 项目管理利器 MAVEN 学习,参考慕课网 :http://www.imooc.com/search/?words=maven 2. 下载MEAVN 地址:http://maven.apache ...

  4. 【js】函数问题

    一.函数重载问题: 由于js的函数传入的参数当做arguments对象(和数组类似,但不是Array的实例),传入的参数类型和数量没有限制,没有函数签名,所以如果要实现重载功能 的话,只能是不够完美得 ...

  5. 服务器 ADO 错误:0x80004005,[DBNETLIB]

    2012-12-0310:44:06 ]ADO 错误:0x80004005,[DBNETLIB][ConnectionOpen(Connect()).]SQL Server 不存在或拒绝访问.[ 20 ...

  6. 1112: [POI2008]砖块Klo

    1112: [POI2008]砖块Klo Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1245  Solved: 426[Submit][Statu ...

  7. 1613: [Usaco2007 Jan]Running贝茜的晨练计划

    1613: [Usaco2007 Jan]Running贝茜的晨练计划 Time Limit: 5 Sec  Memory Limit: 64 MB Submit: 1252  Solved: 609 ...

  8. tp框架之对列表的一系列操作及跳转页面(详细步骤)

    依旧是在Main控制器里面写类方法,如果想看tp全部的话,可以从前几篇开始看,都是一整个步骤下来的 在控制器中重新写个类 然后再做个shouye.html页面 nation表的数据,将会在shou.h ...

  9. 表达式计算 java 后缀表达式

    题目: 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例 ...

  10. 腾讯.NET面试题

    在整个面试过程中,作为面试者的你,角色就是小怪兽,面试官的角色则是奥特曼,更不幸的是,作为小怪兽的你是孤身一人,而奥特曼却往往有好几个~ 以下是网友发的关于腾讯的.NET面试题,不得不说还是有一定的难 ...