`
chenzehe
  • 浏览: 538252 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java.util.concurrent.atomic原子操作类包

 
阅读更多

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

    java.util.concurrent.atomic中的类可以分成4组:

  • 标量类(Scalar):AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  • 数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  • 更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
  • 复合变量类:AtomicMarkableReference,AtomicStampedReference

第一组AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference这四种基本类型用来处理布尔,整数,长整数,对象四种数据,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。如AtomicInteger的实现片断为:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private volatile int value;
public final int get() {
        return value;
}
public final void set(int newValue) {
        value = newValue;
}
public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
  • 构造函数(两个构造函数)
    • 默认的构造函数:初始化的数据分别是false,0,0,null
    • 带参构造函数:参数为初始化的数据
  • set( )和get( )方法:可以原子地设定和获取atomic的数据。类似于volatile,保证数据会在主存中设置或读取
  • void set()和void lazySet():set设置为给定值,直接修改原始值;lazySet延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。
  • getAndSet( )方法
    • 原子的将变量设定为新数据,同时返回先前的旧数据
    • 其本质是get( )操作,然后做set( )操作。尽管这2个操作都是atomic,但是他们合并在一起的时候,就不是atomic。在Java的源程序的级别上,如果不依赖synchronized的机制来完成这个工作,是不可能的。只有依靠native方法才可以。
        public final int getAndSet(int newValue) {
            for (;;) {
                int current = get();
                if (compareAndSet(current, newValue))
                    return current;
            }
        }
  • compareAndSet( ) 和weakCompareAndSet( )方法
    • 这 两个方法都是conditional modifier方法。这2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);如果atomic里面的数据和期望数据一 致,则将新数据设定给atomic的数据,返回true,表明成功;否则就不设定,并返回false。JSR规范中说:以原子方式读取和有条件地写入变量但 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen- before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和 compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。
    public final boolean compareAndSet(int expect, int update) {
    	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    public final boolean weakCompareAndSet(int expect, int update) {
    	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
     
  • 对于 AtomicInteger、AtomicLong还提供了一些特别的方法。
    getAndIncrement( ):以原子方式将当前值加 1,相当于线程安全的i++操作。
    incrementAndGet( ):以原子方式将当前值加 1, 相当于线程安全的++i操作。
    getAndDecrement( ):以原子方式将当前值减 1, 相当于线程安全的i--操作。
    decrementAndGet ( ):以原子方式将当前值减 1,相当于线程安全的--i操作。
    addAndGet( ): 以原子方式将给定值与当前值相加, 实际上就是等于线程安全的i =i+delta操作。
    getAndAdd( ):以原子方式将给定值与当前值相加, 相当于线程安全的t=i;i+=delta;return t;操作。
    以实现一些加法,减法原子操作。(注意 --i、++i不是原子操作,其中包含有3个操作步骤:第一步,读取i;第二步,加1或减1;第三步:写回内存)

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

package thread;
import java.util.concurrent.atomic.AtomicReference;
public class ConcurrentStack<T> {
	private AtomicReference<Node<T>>	stacks	= new AtomicReference<Node<T>>();
	public T push(T e) {
		Node<T> oldNode, newNode;
		for (;;) { // 这里的处理非常的特别,也是必须如此的。
			oldNode = stacks.get();
			newNode = new Node<T>(e, oldNode);
			if (stacks.compareAndSet(oldNode, newNode)) {
				return e;
			}
		}
	}	
	public T pop() {
		Node<T> oldNode, newNode;
		for (;;) {
			oldNode = stacks.get();
			newNode = oldNode.next;
			if (stacks.compareAndSet(oldNode, newNode)) {
				return oldNode.object;
			}
		}
	}	
	private static final class Node<T> {
		private T		object;		
		private Node<T>	next;		
		private Node(T object, Node<T> next) {
			this.object = object;
			this.next = next;
		}
	}	
}

    虽然原子的标量类扩展了Number类,但并没有扩展一些基本类型的包装类,如Integer或Long,事实上他们也不能扩展:基本类型的包装类是不可以修改的,而原子变量类是可以修改的。在原子变量类中没有重新定义hashCode或equals方法,每个实例都是不同的,他们也不宜用做基于散列容器中的键值。 

      第二组AtomicIntegerArray,AtomicLongArray还有AtomicReferenceArray类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方面也引人注目,这对于普通数组来说是不受支持的。

他们内部并不是像AtomicInteger一样维持一个valatile变量,而是全部由native方法实现,如下
AtomicIntegerArray的实现片断:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private static final int scale = unsafe.arrayIndexScale(int[].class);
private final int[] array;
public final int get(int i) {
        return unsafe.getIntVolatile(array, rawIndex(i));
}
public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, rawIndex(i), newValue);
}

 

第三组AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。API非常简单,但是也是有一些约束:

(1)字段必须是volatile类型的

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

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

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

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

 netty5.0中类ChannelOutboundBuffer统计发送的字节总数,由于使用volatile变量已经不能满足,所以使用AtomicIntegerFieldUpdater 来实现的,看下面代码:

//定义
    private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =
            AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");

    private volatile long totalPendingSize;



//使用
        long oldValue = totalPendingSize;
        long newWriteBufferSize = oldValue + size;
        while (!TOTAL_PENDING_SIZE_UPDATER.compareAndSet(this, oldValue, newWriteBufferSize)) {
            oldValue = totalPendingSize;
            newWriteBufferSize = oldValue + size;
        }

 

 

 

分享到:
评论

相关推荐

    java.util.concurrent 学习ppt

    6. **Atomic类**:如AtomicInteger、AtomicLong等,提供原子操作,用于在不使用锁的情况下实现线程安全的变量。 通过Java.util.concurrent包,开发者可以更安全、高效地编写并发程序,避免了重复造轮子,同时也降低...

    java.util.concurrent 测试源文件

    5. **原子类**:Atomic包下的类,如AtomicInteger、AtomicLong等,提供了基于CAS(Compare and Swap,比较并交换)操作的无锁编程机制。这些原子变量类在多线程环境中可以进行高效且线程安全的更新。 6. **并发工具...

    关于 java.util.concurrent 您不知道的 5 件事,第 2 部分

    在Java编程领域,`java.util.concurrent`包是并发编程的核心工具包,提供了高效、线程安全的类和接口,使得开发者能够更容易地处理多线程环境。本篇将深入探讨这个包中一些鲜为人知的知识点,以帮助你提升并发编程的...

    30个常用java工具类

    26. **`java.util.concurrent.atomic`** 包:提供原子操作类,用于线程安全的变量更新。 27. **`java.util.function`** 包:函数式接口,如`Predicate`、`Function`和`Supplier`,支持Lambda表达式。 28. **`java....

    28个java常用的工具类

    21. **`java.util.concurrent.atomic.*`**: 原子变量,用于无锁编程。 22. **`java.util.logging.Logger`**: 日志记录工具。 23. **`java.util.concurrent.locks.Lock`** 和 **`java.util.concurrent.locks....

    java工具类

    24. **`java.util.concurrent.atomic`** 包:包含原子操作类,用于线程安全的变量更新。 25. **`java.util.Calendar`**:旧版的日历类,可以处理复杂的日期和时间计算。 26. **`java.nio.file.Files`** 和 **`java...

    kotlinx.atomicfu,在kotlin中使用原子操作的惯用方法.zip

    在这些平台上,Java的`java.util.concurrent.atomic`包并不适用,而AtomicFu则提供了类似的功能。 原子操作是一种在多线程环境下非常重要的机制,它们能够保证在执行过程中不会被其他线程中断,从而确保了数据的...

    java_util_concurrent_user_guide

    6. **原子类**: `java.util.concurrent.atomic` 包含一系列原子变量类,如`AtomicInteger`、`AtomicLong`、`AtomicReference`等,它们提供了一种无锁编程的方式,支持原子性的读/修改/写操作。 7. **并发工具类**: ...

    java线程-Atomic的含义及示例_.docx

    Java开发工具包(JDK)提供了`java.util.concurrent.atomic`包来处理原子操作的需求。这个包中的类主要用于解决多线程环境中数据的一致性和并发问题,它们通过内部机制确保了操作的原子性,从而避免了显式锁的使用,...

    JAVA_API1.6文档(中文)

    java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...

    工作在同一个java虚拟机中的线程能实现消息互发(alpha)

    5. **原子变量(Atomic Variables)**:`java.util.concurrent.atomic`包提供了原子操作类,如`AtomicInteger`、`AtomicLong`等,它们在多线程环境下提供了无锁的数据更新机制。 6. **管程(Java 5后引入的`java....

    java多线程中的原子操作

    在Java中,`java.util.concurrent.atomic`包提供了多种原子类,如AtomicInteger、AtomicLong等,这些类支持原子性的增加、减小、更新等操作,避免了显式的同步锁的使用,提高了并发性能。例如,AtomicInteger的...

    JavaAPI(官方英文版)

    13. **并发编程(Concurrency)**:`java.util.concurrent`包提供了高效率的并发工具,如`ExecutorService`、`Future`、`Semaphore`等,以及原子变量类(`Atomic*`)。 通过深入学习和理解Java API,开发者可以有效...

    concurrent 源代码

    2. **原子类**: `java.util.concurrent.atomic`子包包含了各种原子操作类,如`AtomicInteger`, `AtomicLong`等,它们提供了在不使用锁的情况下进行原子性更新的机制,提高了并发性能。 3. **同步容器**: `java.util...

    concurrent-1.3.4-sources.jar

    3. **java.util.concurrent.atomic** 包:这个包包含了一系列原子操作类,如AtomicInteger、AtomicLong等。这些类提供了在不使用synchronized关键字的情况下保证线程安全的更新操作,它们基于CAS(Compare and Swap...

    backport-util-concurrent(2.2 /3.1)

    - 虽然Java 5引入了Atomic包,但在backport-util-concurrent中,你可以找到一些基本的原子操作类,如AtomicInteger和AtomicLong,它们提供了无锁的整数和长整型变量操作,提高了并发环境下的性能。 总的来说,...

    java常用包2 81个

    11. `java.util.concurrent.atomic`:提供原子操作类,用于实现线程安全的数据结构,如`AtomicInteger`、`AtomicLong`。 12. `java.util.stream`(Java 8及以上):引入了流式编程,使得数据处理更加简洁,如`...

    J.U.C系列线程安全的理论讲解编程开发技术共6页.pdf

    2. **`java.util.concurrent.atomic`包**:提供了原子变量类,如`AtomicInteger`、`AtomicLong`等,这些类的更新操作是线程安全的,无需额外的同步措施。 3. **`java.util.concurrent.locks`包**:包含锁和条件变量...

    Java API 文档 jdk-17.0.2-doc-all

    13. **内存模型和并发工具**:`java.util.concurrent.atomic`和`java.util.concurrent.locks`包提供了原子变量和锁机制,帮助开发者正确地处理多线程环境下的共享数据。 在"docs"这个压缩包文件中,开发者可以深入...

Global site tag (gtag.js) - Google Analytics