很多情况下我们只需要一个简单的、高效的、线程安全的递增递减方案,而Java中++i或--i并不是线程安全的,但是java.util.concurrent包中提供原子(Atomic) 操作的类,今天我们就来学习它最基本的AtomicInteger。
以下是本文包含的知识点:
1.什么是原子操作
2.AtomicInteger用法
3.CAS介绍
4.AtomicIntegerArray/AtomicIntegerFieldUpdater<T> 介绍
一、什么是原子操作
通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值。在没有额外资源可以利用的情况下,只能使用加锁(synchronized)才能保证读-改-写这三个操作时“原子性”的。
来看下面的例子:
public class AtomicTest { public static int num = 0; public static void increment(){ num++; } public static void main(String[] args) { Thread[] threads = new Thread[20]; for(int i=0; i< threads.length; i++){ threads[i] = new Thread(new Runnable() { public void run() { for(int i=0;i<10000;i++){ increment(); } } }); threads[i].start(); } //等待所有累加线程都结束 while(Thread.activeCount() > 1){ Thread.yield(); } System.out.println("num="+num); } }这段代码发起20个线程,每个线程对变量num进行10000次自增操作,如果这段代码能够正确并发的话,最后输出的结果应该是200000。结果运行之后,发现每次执行它都小于200000,这是为什么呢?
问题就出在自增运算num++中,它其实包含了三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值。这三个操作又不具备原子性操作,一个线程在执行的时候,有可能被其它线程打断,这时num的值就不安全,同时被多个线程共享,修改。
要解决这个问题,其实很简单,加锁就可以了,用synchronized或lock都行。只需要给increment()加上锁即可:
public synchronized static void increment(){ num++; }除了使用同步加锁,JDK5以后还提供了内置的API来解决原子性的自增,自减操作,下面我们来看最基本的AtomicInteger的用法。
二、AtomicInteger用法
AtomicInteger是java.util.concurrent.atomic包中最基本的原子操作类,即int类型的自增、自减原子性操作,我们来看用它实现上面的num自增操作:
public class AtomicIntegerTest { private static AtomicInteger num = new AtomicInteger(0); public static void increment(){ num.incrementAndGet(); } public static void main(String[] args) { Thread[] threads = new Thread[20]; for(int i=0; i< threads.length; i++){ threads[i] = new Thread(new Runnable() { public void run() { for(int i=0;i<100000;i++){ increment(); } } }); threads[i].start(); } //等待所有累加线程都结束 while(Thread.activeCount() > 1){ Thread.yield(); } System.out.println("num="+num); } }执行后,每次运行结果都是200000。而且还不用手动加锁,因为它本身实现的就是线程安全的原子性操作。
AtomicInteger除了incrementAndGet()方法,它还提供其它自增,自减的方法:
decrementAndGet()自减,相当于--i,返回修改后的值
getAndIncrement()自增,相当于++i,返回修改前的值
getAndDecrement()自减,相当于--i,返回修改前的值
getAndSet() 设置值,返回修改前的值
compareAndSet()比较赋值,修改成功返回true,否则返回false
还有其它方法,这些方法都是原子性操作,都是线程安全的。那问题来了,AtomicInteger是怎么保证原子性操作的呢,其实它是利用处理器指令比较并交换(Compare-and-Swap,简称CAS)来实现的。
三、CAS介绍
CAS指令需要3个操作数,分别是内存位置(Java中可理解为内存地址,用V表示),旧的预期值(用A表示)和新值(用B表示)。指令执行时,当且仅当V符合旧的预期值A时,处理器用新值B更新V的值,否则不更新。
在JDK5以后,Java程序才可以使用CAS操作,具体由Unsafe类来实现,并且用户程序无法直接调用,我们可以通过Java API来间接使用它,如J.U.C包里的整数原子类AtomicInteger,其中的compareAndSet()和getAndIncrement都是使用了Unsafe类的CAS操作。
我们来看AtomicInteger的incrementAndGet()源码:
/** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
incrementAndGet()方法在一个无限循环中,不断尝试将一个比自己大1的值赋给自己,如果失败了,那说明在执行“获取-设置”操作的时候已经有了修改,于是再次循环进行下一次操作,直到设置成功为止。
ABA问题:尽管CAS看起很美,但显然这种操作无法涵盖互斥同步的所有使用场景,并且CAS从语义上说并不完美,存在这样一个漏洞:如果一个变量初次读取的时候是A值,并且在准备赋值检查它是仍然是A值,那我们就说它的值没有被其它线程改变过吗?如果在这段期间它的值曾经改成了B,后来又改回成A,那CAS操作就会误以为它从来都没变过,这个漏洞称为CAS操作的ABA问题。如果要解决ABA问题,请使用互斥同步。
四、AtomicIntegerArray/AtomicIntegerFieldUpdater<T> 介绍
AtomicInteger和AtomicLong、AtomicBoolean、AtomicReference差不多,这里就不介绍了。
AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray的API类似,为数组的原子操作类,以AtomicIntegerArray为例来看下:
int addAndGet(int i, int delta) 以原子方式将给定值与索引 i 的元素相加
boolean compareAndSet(int i, int expect, int update) 如果当前值
==
预期值,则以原子方式将位置 i
的元素设置为给定的更新值
int decrementAndGet(int i)以原子方式将索引 i 的元素减 1
int get(int i)获取位置 i 的当前值
int getAndAdd(int i, int delta)以原子方式将给定值与索引 i 的元素相加
int getAndDecrement(int i)以原子方式将索引 i 的元素减 1
int getAndIncrement(int i) 以原子方式将索引 i 的元素加 1
int getAndSet(int i, int newValue)将位置 i 的元素以原子方式设置为给定值,并返回旧值
int incrementAndGet(int i)以原子方式将索引 i 的元素加 1
其实这些方法与AtomicInteger方法也很类似,而且从方法命名就看出是什么意思,这种通过方法、参数的名称就能够得到函数意义的写法是非常值得称赞的。
AtomicIntegerFieldUpdater<T>/AtomicLongFieldUpdater<T>/AtomicReferenceFieldUpdater<T,V>是基于反射的原子更新字段的值。
相应的API也是非常简单的,但是也是有一些约束的。
1.字段必须是volatile类型的!在后面的章节中会详细说明为什么必须是volatile,volatile到底是个什么东西。
2.字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
3.只能是实例变量,不能是类变量,也就是说不能加static关键字。
4.只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
5.对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。
public class AtomicIntegerFieldUpdaterTest { class DemoData { public volatile int value1 = 1; volatile int value2 = 2; protected volatile int value3 = 3; private volatile int value4 = 4; } AtomicIntegerFieldUpdater<DemoData> getUpdater(String fieldName) { return AtomicIntegerFieldUpdater.newUpdater(DemoData. class, fieldName); } void doit() { DemoData data = new DemoData(); System. out.println("1 ==> " + getUpdater("value1" ).addAndGet(data, 10)); System. out.println("2 ==> " + getUpdater( "value2").incrementAndGet(data)); System. out.println("3 ==> " + getUpdater( "value3").decrementAndGet(data)); System. out.println("value4 ==> " + getUpdater( "value4").compareAndSet(data, 4, 5)); } public static void main(String[] args) { //DemoData的字段value3/value4对于AtomicIntegerFieldUpdaterDemo类是不可见的,因此通过反射是不能直接修改其值的。 AtomicIntegerFieldUpdaterTest demo = new AtomicIntegerFieldUpdaterTest(); demo.doit(); } }执行结果为:
1 ==> 11 2 ==> 3 Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalAccessException : Class org.concurrent.atomic.AtomicIntegerFieldUpdaterTest can not access a protected member of class org.concurrent.atomic.AtomicIntegerFieldUpdaterTest$DemoData using an instance of org.concurrent.atomic.AtomicIntegerFieldUpdaterTest$DemoData看到只改变了value1,value2的值
value3value4对于AtomicIntegerFieldUpdaterDemo类是不可见的,因此通过反射是不能直接修改其值
相关推荐
Java 并发编程在现代软件开发中占据重要地位,尤其是在多核处理器的时代。JUC(java.util.concurrent)库是 Java 标准库的一部分,提供了丰富的多线程并发工具,旨在帮助开发者编写高性能、高可伸缩性的并发程序。...
│ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...
Java并发编程常见知识点源码集锦,涉及到对象锁,Executors多任务线程框架,线程池等示例,列出一些源码包中包括的内容: volatile关键字的非原子性、volatile关键字的使用、AtomicInteger原子性操作、线程安全小...
│ Java并发编程.png │ ppt+源码.rar │ 高并发编程第二阶段01讲、课程大纲及主要内容介绍.wmv │ 高并发编程第二阶段02讲、介绍四种Singleton方式的优缺点在多线程情况下.wmv │ 高并发编程第二阶段03讲、...
1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...
AtomicInteger示例AtomicInteger用于原子增量计数器之类的应用程序。 简短的示例代码: public class AtomicIntegerExample { private final ExecutorService execService = Executors . newFixedThreadPool( 100 );...
1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...
AtomicInteger是java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作。下面小编和大家一起学习一下
java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...
1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...
JUC(Java Util Concurrent)是Java中用于并发编程的工具包,提供了一组接口和类,用于处理多线程和并发操作。JUC提供了一些常用的并发编程模式和工具,如线程池、并发集合、原子操作等。 JUC的主要特点包括: ...
AtomicInteger atomicInteger = new AtomicInteger(5); atomicInteger.compareAndSet(5, 2020) + \t current data is + atomicInteger.get()) /** * Atomically sets the value to the given updated value * if ...
测试java.util.concurrent.atomic.AtomicInteger的类 与直接使用int做区别
主要介绍了Java中对AtomicInteger和int值在多线程下递增操作的测试,本文得出AtomicInteger操作 与 int操作的效率大致相差在50-80倍上下的结论,需要的朋友可以参考下
本文介绍了多线程环境下自增效率比较及原理解析。在多线程环境下,对于...适用于Java开发人员和多线程编程爱好者等人群,内容关键词包括多线程、自增、synchronized、AtomicInteger、LongAdder、LongAccumulator等。
文章目录引出问题(代码示例)问题原理说明问题解决方法1:使用锁机制方法2:原子类AtomicInteger原子类CAS机制实现线程安全概述源码分析CAS与Synchronized:乐观锁,悲观锁。 概述:所谓的原子性是指在一次操作或者...
主要介绍了Java AtomicInteger类的使用方法详解,文中有具体实例代码,具有一定参考价值,需要的朋友可以了解下。
3.基于AtomicInteger的CAS机制; 4.使用Redis作为原子计数器(watch事务+decr操作),RabbitMQ作为消息队列记录用户抢购行为,MySQL做异步存储。 上述四个解决方案均使用了JMeter进行压力与性能测试(实验设置的是10...
主要介绍了Java AtomicInteger类使用方法实例讲解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...