实现全局自增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. everything 快速搜索有代价

    我在一台电脑上运行过everything后,把它拷贝到另一台电脑上运行,前一台电脑上的搜索结果记录居然还,包括文件类型,标题,大小,位置.天呐··· 虽然看不到内容,但对于一个社工来说,一个标题完全足 ...

  2. Hibernate框架注解

    1.使用Hibernate注解的步骤                1.添加jar包                            Hibernate-annotations.jar      ...

  3. Java中代理对象的使用小结

    在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式.装饰模式等. 代理对象内部含有对真实对象的引用,从而 ...

  4. shell [ff: 未找到命令

    在学习shell脚本时遇到一个问题:  [ff: 未找到命令 相信很多初学者都会遇到,再次说明一下,希望对大家有所帮助: shell脚本代码如下: #!/bin/bash echo -n " ...

  5. 第一个小程序:helloWord

    public class HelloWorld {     public static void main(String []args){          System.out.println(&q ...

  6. swift -- 基础

    swift -- 基础 1.常量和变量 常量: let 变量: var 2.声明常量和变量 常量的声明: let let  a = 1         //末尾可以不加分号,等号两边的空格必须对应(同 ...

  7. Go语言并发机制初探

    Go 语言相比Java等一个很大的优势就是可以方便地编写并发程序.Go 语言内置了 goroutine 机制,使用goroutine可以快速地开发并发程序, 更好的利用多核处理器资源.这篇文章学习 g ...

  8. js面向对象总结

    原型链 新创建的xiaoming的原型链是: xiaoming ----> Student.prototype ----> Object.prototype ----> null 也 ...

  9. SQL Tuning 基础概述10 - 体会索引的常见执行计划

    在<SQL Tuning 基础概述05 - Oracle 索引类型及介绍>的1.5小节,提到了几种"索引的常见执行计划": INDEX FULL SCAN:索引的全扫描 ...

  10. 强大的版本管理工具 Git

    Git 简介 git 是一款免费.开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目.是 linux 创始人 Linus Torvalds 为了帮助管理 linux 内核开发而开发的一个开 ...