java.util.concurrent.atomic
中的都是原子类,具有不可分割的性质,即一个操作是不可中断的,即便是多线程的情况下也可以保证。
原子类的作用和锁类似,是为了保证并发情况下线程安全。但原子类相比于锁,有一定的优势:
原子类分类 | 实现类 |
---|---|
Atomic*基本类型原子类 | Atomiclnteger、AtomicLong、AtomicBoolean |
Atomic*Array数组类型原子类 | AtomiclntegerArray、AtomicLongArray、AtomicReferenceArray、 |
Atomic*Reference引用类型原子类 | AtomicReference、AtomicStampedReference、AtomicMarkableReferenceAtomic |
Atomic*FieldUpdater升级类型原子类 | Atomiclntegerfieldupdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater |
Adder累加器 | LongAdder、DoubleAdder |
Accumulator累加器 | LongAccumulator、DoubleAccumulator |
public final int get()
:获取当前的值
public final int getAndSet(int newValue)
:获取当前的值,并设置新的值
public final int getAndIncrement()
:获取当前的值,并自增
public final int getAndDecrement()
:获取当前的值,并自减
public final int getAndAdd(int delta)
:获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update)
:如果输入的数值等于预期值,则以原子方式将该值设置为输入值( update )
如果仅仅是对数值需要保证线程安全的情况,可以使用AtomicInteger类
正常的操作变量是: 1. 读取 2. 进行改变 3. 再写回
public class AtomicIntegerDemo implements Runnable{
private static final AtomicInteger atomicInt = new AtomicInteger();
private static volatile int basicInt = 0;
private void incrementAtomic(){
atomicInt.getAndIncrement();
}
//可以通过加synchronized来解决问题,但是如果方法内容增多,而且本来上锁就是及其耗费资源的操作
private void incrementBasic(){
basicInt++;
}
public static void main(String[] args) {
AtomicIntegerDemo r = new AtomicIntegerDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInt.get());
System.out.println(basicInt);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}
}
对整个数组进行加减操作,可以看到这个数组原子类是有效果的。
public class AtomicArrayDemo {
//验证原子性
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new
AtomicIntegerArray(1000);
Incrementer incrementer = new Incrementer(atomicIntegerArray);
Decrementer decrementer = new Decrementer(atomicIntegerArray);
Thread[] threadsIncre = new Thread[100];
Thread[] threadsDecre = new Thread[100];
for (int i = 0; i < 100; i++) {
threadsIncre[i] = new Thread(incrementer);
threadsDecre[i] = new Thread(decrementer);
threadsIncre[i].start();
threadsDecre[i].start();
}
for (int i = 0; i < 100; i++) {
try {
threadsIncre[i].join();
threadsDecre[i].join();
}catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < atomicIntegerArray.length(); i++) {
if (atomicIntegerArray.get(i) != 0){
System.out.println("wrong");
}
}
System.out.println("over");
}
}
class Decrementer implements Runnable {
private AtomicIntegerArray array;
public Decrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndDecrement(i);
}
}
}
class Incrementer implements Runnable {
private AtomicIntegerArray array;
public Incrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndIncrement(i);
}
}
}
比如在做账或者财务管理,经常会有并发的修改,需要原子性且数据量又大,那么就可以将数组转换成 Atomic*Array
这里有一个构造方法,可以转换数组
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
compareAndSet(V expect, V update)
把比较和设置合并为一个原子操作,可以原子的改变一个引用。
private AtomicReference<Thread> sign = new AtomicReference<>();
public void lock(){
Thread cur = Thread.currentThread();
//通过引用原子类来达到上锁的目的
while (!sign.compareAndSet(null,cur)){
System.out.println("失败");
}
}
用AtomicIntegerFieldUpdater升级普通变量
如果需要并发安全可以直接使用原子类,但是
效果和正常原子变量是一样的
public class FieldUpdaterDemo implements Runnable {
static Candidate normalOne;
static Candidate updateOne;
public static AtomicIntegerFieldUpdater<Candidate> scoerUpdater =
AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
public static void main(String[] args) throws InterruptedException {
normalOne = new Candidate();
updateOne = new Candidate();
FieldUpdaterDemo r = new
FieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量:" + normalOne.score);
System.out.println("升级后的结果" + updateOne.score);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
normalOne.score++;
scoerUpdater.getAndIncrement(updateOne);
}
}
public static class Candidate {
volatile int score;
}
}
AtomicIntegerFieldUpdater
使用newUpdater方法返回一个构造的一个静态final内部原子对象
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
而这个内部类中的构造方法中使用该类类对象反射调用了getDeclaredField,即获取该变量,不考虑修饰符。但是注意,private是不能被获取到的。
通过反射操作类的私有(private)成员变量时,需要通过
field.setAccessible(true)
将字段设置为可以访问的。
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
是Java 8引入的,相对是比较新的一个类。
高并发下LongAdderkAtomicLong效率高,不过本质是空间换时间。
竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,比如1.7的ConcurrentHashMap使用16段segment,提高了并发性。
这里演示多线程情况下AtomicLong的性能,有16个线程对同一个AtomicLong累加。
public class AdderTest {
//演示longadder比atomiclong性能好
public static void main(String[] args) {
//AtomicLong counter = new AtomicLong(0);
LongAdder counter = new LongAdder();
ExecutorService service = Executors
.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
//这里通过判断是否终止来打印结果,shutdown可详见线程池章节
service.shutdown();
while (!service.isTerminated()) {
}
//counter.get()
System.out.println(counter.sum());
System.out.println(System.currentTimeMillis()-start);
}
private static class Task implements Runnable {
private LongAdder counter;
public Task(LongAdder counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
//counter.incrementAndGet
counter.increment();
//就是add(1)
}
}
}
}
这里可以去学习以下缓存一致性,volatile原理
由于竞争很激烈,每一次加法,都要flush和refresh,导致很耗费资源。
在内部,这个LongAdder的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次加法都需要做同步,所以在高并发的时候会导致冲突比较多,也就降低了效率。
而jdk8新增的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内计数,这样一来就不会和其他线程的计数器干扰。
第一个线程的计数器数值,ctr’,为1的时候,可能线程2的计数器ctr’’的数值已经是3了,他们之间并不存在竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要刚才的flush和refresh。这里也没有一个公共的counter来给所有线程统一计数。
使用空间换时间
这里看到有一个小缺陷,因为没有上锁,所以在遍历还没有结束之前,遍历过的数组元素的变动无法体现在结果中
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
//cell数组不为空,则遍历累加
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
//cell为空直接返回只取了base的sum,不为空则返回base加上遍历cell数组的值
return sum;
}
compareAndSet(long expect, long update)
。Accumulator和Adder非常相似,Accumulator就是一个更通用版本的Adder。
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0);
ExecutorService executor = Executors.newFixedThreadPool(6);
accumulator.accumulate(1);
System.out.println(accumulator.getThenReset());
IntStream.range(1,10).forEach(i ->executor.submit(()->accumulator.accumulate(i)));
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
在构造一个Accumulator的时候可以指定任意的二元操作。比如Math.max(a,b),各种加减乘除。
其中LongBinaryOperator是一个函数式接口,那么我们就可以使用函数调用或者lambda表达式来简化代码。
public LongAccumulator(LongBinaryOperator accumulatorFunction,
long identity) {
this.function = accumulatorFunction;
base = this.identity = identity;
}
for (int i = 0; i < 1000; i++) {
x += y;
}
使用线程池可以将这种操作并行运算,但是要注意适合于操作无关乎计算顺序的任务场景