在java.util.concurrent.atomic
包下有Java
提供的线程安全的原子类。
原子类内部使用volatile
确保可见性和有序性,使用Unsafe
提供的CAS
方法确保原子性。
下面通过计数器的例子介绍原子类AtomicInteger
的使用并简单的分析AtomicInteger
的部分源码
package com.rjh.threadsafe;
/**
* 非线程安全的计数器类
* @author RJH
* 2017年11月30日
*/
public class Counter {
/**
* 从0开始计数
*/
private int i=0;
/**
* 不断自增
* @author RJH
* @return
*/
public int increase(){
return ++i;
}
/**
* 输出当前计数器的值
* @author RJH
* @return
*/
public int currentCount(){
return i;
}
}
运行测试用例代码的时候可以多次运行并比较输出结果,如果输出结果较为接近,这样的输出结果就会相对准确。
package com.rjh.threadsafe;
/**
* 计数器测试类
* @author RJH
* 2017年11月30日
*/
public class CounterTest {
public static void main(String[] args) {
//构建一个计数器实例
Counter counter=new Counter();
//线程个数
int threadNum=1000;
//每个线程的点击次数
int times=10000;
long start=System.currentTimeMillis();
for(int i=0;i1){
}
System.out.println("TotalTime:"+(System.currentTimeMillis()-start));
System.out.println("Count:"+counter.currentCount());
}
/**
* 点击线程,模拟点击事件触发计数器
* @author RJH
* 2017年11月30日
*/
private static class ClickThread extends Thread{
/**
* 计数器
*/
private Counter counter;
/**
* 点击总次数
*/
private int times;
public ClickThread(Counter counter,int times) {
this.counter = counter;
this.times=times;
}
@Override
public void run(){
for(int i=0;i
Count
应该为threadNum*times
,即1000X10000=10000000
TotalTime:56
Count:9947180
在例子中的++i
不是原子性的,这段代码执行了3
步操作:
i
的旧值i
的值i
的新值而在实际运行的时候,由于可能存在多个线程读取相同的i的旧值并在修改后写入,所以出现与预期结果不一致的情况。
接下来在非线程安全的计数器类上改为使用原子类AtomicIntege
r把该计数器类改为线程安全的。
package com.rjh.threadsafe;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程安全的计数器类
* @author RJH
* 2017年11月30日
*/
public class Counter {
/**
* 从0开始计数
*/
private AtomicInteger i=new AtomicInteger(0);
/**
* 不断自增
* @author RJH
* @return
*/
public int increase(){
return i.incrementAndGet();
}
/**
* 输出当前计数器的值
* @author RJH
* @return
*/
public int currentCount(){
return i.get();
}
}
Count
应该为threadNum*times,即1000X10000=10000000
TotalTime:625
Count:10000000
使用原子类AtomicInteger
虽然确保了线程安全,但是明显看出花费了更多的时间。后面我会对AtomicInteger
的addAndGet()
方法进行源码分析。
/**
* 以原子操作的方式将当前值加 1。
* @return 更新后的值
*/
public final int incrementAndGet() {
for (;;) {//等价于while(true)
int current = get();//获取当前的值
int next = current + 1;//自增后为更新后的值
if (compareAndSet(current, next))//如果更新成功则返回更新后的值(循环结束),否则进入下一次循环
return next;
}
}
/**
* 如果当前值等于期待的值则把将期待的值以原子操作的方式改为更新后的值
*
* @param 当前期待的值
* @param 更新后的值
* @return 更新成功返回true,如果当前实际的值不等于期待的值则返回false
*/
public final boolean compareAndSet(int expect, int update) {
//Unsafe类提供CAS,valueOffset是使用Unsafe.objectFieldOffset()方法获取到的字段偏移量
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
每次CAS
失败都会去获取当前的值作为期待值,而更新是由CAS
确保原子性。Java中的无锁编程基本都是使用volatile
结合Unsafe
的CAS
确保线程安全,但是CAS
无法处理ABA
问题。而且CAS
会带来更大的CPU
开销。
线程安全计数器花费更多的时间是因为CAS
失败导致的。
虽然只是介绍了AtomicInteger
,但是其实其他的原子类内部实现的原理与AtomicInteger
类似。