`

Java多线程(二)之Atomic:原子变量与原子类

 
阅读更多

一、何谓Atomic?

 

 Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位。计算机中的Atomic是指不能分割成若干部分的意思。如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的。通常来说,原子指令由硬件提供,供软件来实现原子方法(某个线程进入该方法后,就不会被中断,直到其执行完成)

 

 在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK",经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。

 

、java.util.concurrent中的原子变量

 

 

 

无论是直接的还是间接的,几乎 java.util.concurrent 包中的所有类都使用原子变量,而不使用同步。类似 ConcurrentLinkedQueue 的类也使用原子变量直接实现无等待算法,而类似 ConcurrentHashMap 的类使用 ReentrantLock 在需要时进行锁定。然后, ReentrantLock 使用原子变量来维护等待锁定的线程队列。

如果没有 JDK 5.0 中的 JVM 改进,将无法构造这些类,这些改进暴露了(向类库,而不是用户类)接口来访问硬件级的同步原语。然后,java.util.concurrent 中的原子变量类和其他类向用户类公开这些功能

 

java.util.concurrent.atomic的原子类

 

这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。其中的类可以分成4组

  • AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • AtomicIntegerArray,AtomicLongArray
  • AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
  • AtomicMarkableReference,AtomicStampedReference,AtomicReferenceArray

其中AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference是类似的。

首先AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference内部api是类似的:举个AtomicReference的例子

 

使用AtomicReference创建线程安全的堆栈

 

Java代码  收藏代码
  1. public class LinkedStack<T> {  
  2.   
  3.     private AtomicReference<Node<T>> stacks = new AtomicReference<Node<T>>();  
  4.   
  5.     public T push(T e) {  
  6.         Node<T> oldNode, newNode;  
  7.         while (true) { //这里的处理非常的特别,也是必须如此的。  
  8.             oldNode = stacks.get();  
  9.             newNode = new Node<T>(e, oldNode);  
  10.             if (stacks.compareAndSet(oldNode, newNode)) {  
  11.                 return e;  
  12.             }  
  13.         }  
  14.     }  
  15.       
  16.     public T pop() {  
  17.         Node<T> oldNode, newNode;  
  18.         while (true) {  
  19.             oldNode = stacks.get();  
  20.             newNode = oldNode.next;  
  21.             if (stacks.compareAndSet(oldNode, newNode)) {  
  22.                 return oldNode.object;  
  23.             }  
  24.         }  
  25.     }  
  26.   
  27.     private static final class Node<T> {  
  28.         private T object;  
  29.           
  30.         private Node<T> next;  
  31.   
  32.         private Node(T object, Node<T> next) {  
  33.             this.object = object;  
  34.             this.next = next;  
  35.         }  
  36.     }  
  37. }  
  

 

然后关注字段的原子更新。

AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdater<T,V>是基于反射的原子更新字段的值。

相应的API也是非常简单的,但是也是有一些约束的。

(1)字段必须是volatile类型的!volatile到底是个什么东西。请查看 http://blog.csdn.net/a511596982/article/details/8201744

(2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。

(3)只能是实例变量,不能是类变量,也就是说不能加static关键字。

(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。

(5)对于AtomicIntegerFieldUpdaterAtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater

 

在下面的例子中描述了操作的方法。

 

[java] view plaincopy
 
  1. import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;   
  2.   
  3. public class AtomicIntegerFieldUpdaterDemo {   
  4.   
  5.    class DemoData{  
  6.        public volatile int value1 = 1;  
  7.        volatile int value2 = 2;  
  8.        protected volatile int value3 = 3;  
  9.        private volatile int value4 = 4;  
  10.    }  
  11.     AtomicIntegerFieldUpdater<DemoData> getUpdater(String fieldName) {  
  12.         return AtomicIntegerFieldUpdater.newUpdater(DemoData.class, fieldName);  
  13.     }  
  14.     void doit() {  
  15.         DemoData data = new DemoData();  
  16.         System.out.println("1 ==> "+getUpdater("value1").getAndSet(data, 10));  
  17.         System.out.println("3 ==> "+getUpdater("value2").incrementAndGet(data));  
  18.         System.out.println("2 ==> "+getUpdater("value3").decrementAndGet(data));  
  19.         System.out.println("true ==> "+getUpdater("value4").compareAndSet(data, 45));  
  20.     }  
  21.     public static void main(String[] args) {  
  22.         AtomicIntegerFieldUpdaterDemo demo = new AtomicIntegerFieldUpdaterDemo();  
  23.         demo.doit();  
  24.     }  
  25. }   

 


在上面的例子中DemoData的字段value3/value4对于AtomicIntegerFieldUpdaterDemo类是不可见的,因此通过反射是不能直接修改其值的。

 

AtomicMarkableReference类描述的一个<Object,Boolean>的对,可以原子的修改Object或者Boolean的值,这种数据结构在一些缓存或者状态描述中比较有用。这种结构在单个或者同时修改Object/Boolean的时候能够有效的提高吞吐量。

 

AtomicStampedReference类维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。对比AtomicMarkableReference类的<Object,Boolean>,AtomicStampedReference维护的是一种类似<Object,int>的数据结构,其实就是对对象(引用)的一个并发计数。但是与AtomicInteger不同的是,此数据结构可以携带一个对象引用(Object),并且能够对此对象和计数同时进行原子操作。

在本文结尾会提到“ABA问题”,而AtomicMarkableReference/AtomicStampedReference在解决“ABA问题”上很有用

三、Atomic类的作用

 
  • 使得让对单一数据的操作,实现了原子化
  • 使用Atomic类构建复杂的,无需阻塞的代码
    • 访问对2个或2个以上的atomic变量(或者对单个atomic变量进行2次或2次以上的操作)通常认为是需要同步的,以达到让这些操作能被作为一个原子单元。
  • 无锁定且无等待算法

     

    基于 CAS (compare and swap)的并发算法称为 无锁定算法,因为线程不必再等待锁定(有时称为互斥或关键部分,这取决于线程平台的术语)。无论 CAS 操作成功还是失败,在任何一种情况中,它都在可预知的时间内完成。如果 CAS 失败,调用者可以重试 CAS 操作或采取其他适合的操作。

    如果每个线程在其他线程任意延迟(或甚至失败)时都将持续进行操作,就可以说该算法是 无等待的。与此形成对比的是, 无锁定算法要求仅 某个线程总是执行操作。(无等待的另一种定义是保证每个线程在其有限的步骤中正确计算自己的操作,而不管其他线程的操作、计时、交叉或速度。这一限制可以是系统中线程数的函数;例如,如果有 10 个线程,每个线程都执行一次CasCounter.increment() 操作,最坏的情况下,每个线程将必须重试最多九次,才能完成增加。)

    再过去的 15 年里,人们已经对无等待且无锁定算法(也称为 无阻塞算法)进行了大量研究,许多人通用数据结构已经发现了无阻塞算法。无阻塞算法被广泛用于操作系统和 JVM 级别,进行诸如线程和进程调度等任务。虽然它们的实现比较复杂,但相对于基于锁定的备选算法,它们有许多优点:可以避免优先级倒置和死锁等危险,竞争比较便宜,协调发生在更细的粒度级别,允许更高程度的并行机制等等。

    常见的:

    非阻塞的计数器Counter 
    非阻塞堆栈ConcurrentStack
    非阻塞的链表ConcurrentLinkedQueue

    非阻塞算法简介:http://www.ibm.com/developerworks/cn/java/j-jtp04186/

     

分享到:
评论

相关推荐

    java多线程_java多线程下变量共享_

    `Atomic`类:Java并发包(`java.util.concurrent.atomic`)提供了如`AtomicInteger`、`AtomicLong`等原子类,它们提供了原子操作,如`incrementAndGet()`,在多线程环境下可以替代`synchronized`和`volatile`,实现...

    Java多线程Atomic包操作原子变量与原子类详解

    Java的`Atomic`包是Java多线程编程中一个重要的工具,它提供了对原子变量的操作,确保了在并发环境下的数据一致性。在多线程环境中,原子性是至关重要的,这意味着一个操作要么完整执行,要么不执行,不会被其他线程...

    Java多线程编程核心技术_完整版_java_

    3. Atomic*类:如AtomicInteger、AtomicLong等,提供原子操作的变量,适用于简单的同步需求。 七、线程优先级与调度 1. 线程优先级:Java定义了三个基本优先级(MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY),线程...

    深入学习:Java多线程编程

    8. **线程安全的编程实践**:书中可能会讨论如何编写线程安全的代码,包括避免使用静态变量、合理使用volatile关键字、理解原子操作(Atomic类)以及使用并发设计模式。 9. **JVM与多线程**:理解JVM的内存模型...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    java多线程设计

    5. 原子操作(Atomic):AtomicInteger、AtomicLong等原子类提供了无锁编程的支持,保证了在多线程环境下的原子性操作。 总结,Java多线程设计是构建高性能、高并发应用的基础。通过理解并合理使用不可变对象,我们...

    Java多线程编程总结

    - `java.util.concurrent.atomic` 包提供了原子类,用于在不需要锁的情况下进行线程安全的操作。 #### 二十、Java线程:新特征-障碍器 - `CyclicBarrier` 和 `Phaser` 类允许一组线程相互等待,直到所有线程到达一...

    JAVA多线程总结

    本篇总结涵盖了Java多线程的基础概念、创建与启动、线程调度、同步与协作以及新特性。 **一、Java线程:概念与原理** 1. **线程与进程**: - **进程**:是操作系统资源分配的基本单位,每个进程都有独立的内存...

    Java多线程运算集合

    #### 二、Java多线程的创建与启动 - **定义线程的方法**: - **继承 `java.lang.Thread` 类**:创建一个新类继承自 `Thread` 类,并重写 `run()` 方法。 - **实现 `java.lang.Runnable` 接口**:创建一个实现了 `...

    Java多线程.pdf

    Java多线程是Java编程中一个非常重要的概念,它允许程序在同一时间执行多个任务,从而提高了应用程序的效率和响应速度。在操作系统中,程序被加载到内存中形成进程,而进程中的执行单元就是线程。Java多线程的实现...

    深入浅出Java多线程.pdf

    ### 深入浅出Java多线程.pdf #### 知识点概览 本PDF文档涉及了Java多线程的全面介绍,分为基础篇、原理篇和JDK工具篇三个部分,旨在帮助读者深入了解Java多线程的概念、原理及实践应用。 #### 基础篇 **1. 进程...

    java实现多线程下载

    - **进度跟踪**:每个线程下载完成后,需要更新总的下载进度,这可以通过共享变量或者`java.util.concurrent.atomic`包中的原子类实现。 - **合并文件**:所有线程下载完成后,将各个部分合并成完整的文件。这通常...

    java多线程笔记全手打

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,提高了程序的运行效率和资源利用率。本笔记全面涵盖了多线程的学习,包括基础理论和实践代码,旨在帮助开发者深入理解并掌握Java多线程技术。 一、...

    Java多线程与线程安全实践-基于Http协议的断点续传.zip

    总的来说,Java多线程与线程安全实践对于理解和实现基于HTTP协议的断点续传功能至关重要。通过合理地运用多线程、同步机制和原子变量,可以构建出高效、安全的并发应用程序。这个实践项目会深入讲解这些概念,并提供...

    Java多线程之进阶篇(二).docx

    Java的并发模型就是基于线程的,它利用了Java内存模型(JMM)和特定的并发工具类,如`synchronized`关键字、`volatile`变量以及`java.util.concurrent.atomic`包下的原子类,来确保线程安全。 `java.util....

    多线程demo/java多线程练习

    本项目"多线程demo/java多线程练习"旨在通过实际操作来深入理解和掌握多线程技术,同时模拟数据库操作,这在现代应用程序开发中至关重要。 1. **线程基础** - **创建线程**:Java提供了两种创建线程的方式,分别是...

    Java 多线程 订票 示例 线程安全

    总结起来,Java多线程在实现订票系统时,需要注意线程安全问题,通过合理使用同步机制、Lock接口和原子类,可以在保证数据一致性的同时提高并发性能。在实际开发中,要根据业务场景选择合适的线程安全策略,以达到...

    Java多线程实战精讲-带你一次搞明白Java多线程高并发

    Java多线程实战精讲是Java开发者必备的技能之一,特别是在处理高并发场景时,它的重要性不言而喻。本文将深入探讨Java多线程的相关知识点,帮助你全面理解并掌握这一核心概念。 1. **线程基础** - **线程定义**:...

    Java多线程与线程安全实践-基于Http协议的断点续传

    2. **原子操作**:使用`java.util.concurrent.atomic`包下的原子类,如`AtomicInteger`,它们提供了一种在无锁情况下更新变量的方法,确保操作的原子性。 3. **不可变对象**:如果对象在创建后其状态不能被修改,...

    JAVA多线程与线程安全实践-基于Http协议的断点续传.rar

    本项目"JAVA多线程与线程安全实践-基于Http协议的断点续传"探讨了如何在Java中实现多线程以及如何确保线程安全,特别是在处理HTTP协议的断点续传功能时。 断点续传是文件传输的一种优化技术,允许用户在不重新下载...

Global site tag (gtag.js) - Google Analytics