1 什麼是原子類,有什麼作用?
- 不可分割
- 一個操作是不可中斷的,即便是多線程的情況下也可以保證
- java.util.concurrent.atomic
- 原子類的作用和鎖類似,是爲了保證併發情況下線程安全
- 粒度更細:原子變量可以把競爭範圍縮小到變量級別,這是我們可以獲得的最細粒度的情況了,通常鎖的粒度都要大於原子變量的粒度(原子類相比於鎖,有一定的優勢)
- 效率更高:通常,使用原子類的效率會比使用鎖的效率更高,除了高度競爭的情況
2 6類原子類縱覽
3 Atomic*基本類型原子類,AtomicInteger爲例
- AtomicInteger:整形原子類
- AtomicLong:長整型原子類
- AtomicBoolean:布爾型原子類
AtomicInteger常用方法
- public final int get() //取當前的值
- public final int getAndSet(int newValue) //獲取當前的值,並設置新的值
- public final int getAndIncrement() //取當前的值,並自增
- public final int getAndDecrement() //獲取當前的值,並自減
- public final int getAndAdd(int delta) //獲取當前的值,並加上預期的值
- public final boolean compareAndSet(int expect, int update) //如果輸入的數值等於預期值,則以原子方式將該值設置爲輸入值(update)
/**
* 演示AtomicInteger的基本用法,對比非原子類的線程安全問題,使用了原子類之後,不需要加鎖,也可以保證線程安全。
*/
public class AtomicIntegerDemo1 implements Runnable {
private static final AtomicInteger atomicInteger = new AtomicInteger();
private static volatile int basicCount = 0;
public void incrementAtomic() {
//正數就是每次都相加delta getAndAdd(1) == getAndIncrement()
//負數就是每次都相減delta getAndAdd(-1) == getAndDecrement()
atomicInteger.getAndAdd(1);
}
//public synchronized void incrementBasic() {
// basicCount++;
//}
public void incrementBasic() {
basicCount++;
}
public static void main(String[] args) throws InterruptedException {
AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("原子類的結果:" + atomicInteger.get());
System.out.println("普通變量的結果:" + basicCount);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}
}
4 AtomicBoolean
public class AtomicAtomicBooleanExample {
private static AtomicBoolean isHappened = new AtomicBoolean(false);
// 請求總數
public static int clientTotal = 5000;
// 同時併發執行的線程數
public static int threadTotal = 200;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch begin = new CountDownLatch(1);
for (int i = 0; i < clientTotal; i++) {
executorService.execute(() -> {
try {
begin.await();
//System.out.println(Thread.currentThread().getName() + "開始執行test方法");
test();
} catch (InterruptedException e) {
System.out.println("interruptedException");
}
});
}
Thread.sleep(3000);
begin.countDown();
executorService.shutdown();
System.out.println("isHappened:" + isHappened.get());
}
private static void test() {
if (isHappened.compareAndSet(false, true)) {
//裏面的方法只會執行一次
System.out.println("execute");
}
}
}
4 Atomic*Array數組類型原子類
/**
* 演示原子數組的使用方法
*/
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[] threadsIncrementer = new Thread[100];
Thread[] threadsDecrementer = new Thread[100];
for (int i = 0; i < 100; i++) {
threadsDecrementer[i] = new Thread(decrementer);
threadsIncrementer[i] = new Thread(incrementer);
threadsDecrementer[i].start();
threadsIncrementer[i].start();
}
//Thread.sleep(10000);
for (int i = 0; i < 100; i++) {
try {
threadsDecrementer[i].join();
threadsIncrementer[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < atomicIntegerArray.length(); i++) {
//if (atomicIntegerArray.get(i)!=0) {
// System.out.println("發現了錯誤"+i);
//}
System.out.println(atomicIntegerArray.get(i));
}
System.out.println("運行結束");
}
}
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);
}
}
}
5 Atomic*Reference引用類型原子類
AtomicReference:AtomicReference類的作用,和AtomicInteger並沒有本質區別,AtomicInteger可以讓一個整數保證原子性,而AtomicReference可以讓一個對象保證原子性,當然,AtomicReference的功能明顯比AtomicInteger強,因爲一個對象裏可以包含很多屬性。用法和AtomicInteger類似。
@Slf4j
public class AtomicExample4 {
private static AtomicReference<Integer> count = new AtomicReference<>(0);
public static void main(String[] args) {
count.compareAndSet(0, 2); // 2
count.compareAndSet(0, 1); // no
count.compareAndSet(1, 3); // no
count.compareAndSet(2, 4); // 4
count.compareAndSet(3, 5); // no
log.info("count:{}", count.get());//4
}
}
6 把普通變量升級爲原子類:用AtomicIntegerFieldUpdater升級原有變量
- AtomicIntegerFieldUpdater對普通變量進行升級
- 使用場景:偶爾需要一個原子get-set操作
/**
* 演示AtomicIntegerFieldUpdater的用法
*/
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}
//候選人
public static class Candidate {
//分數
volatile int score;
}
public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通變量:"+peter.score);
System.out.println("升級後的結果"+ tom.score);
}
}
@Slf4j
public class AtomicExample5 {
//原子性的去更新某一個類的實例指定的某一個字段
private static AtomicIntegerFieldUpdater<AtomicExample5> updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample5.class, "count");
//volatile修飾的非static的字段
@Getter
public volatile int count = 100;
public static void main(String[] args) {
AtomicExample5 example5 = new AtomicExample5();
//如果example5裏面的count是100就更新爲120
if (updater.compareAndSet(example5, 100, 120)) {
log.info("update success 1, {}", example5.getCount());
}
if (updater.compareAndSet(example5, 100, 120)) {
log.info("update success 2, {}", example5.getCount());
} else {
log.info("update failed, {}", example5.getCount());
}
}
}
AtomicIntegerFieldUpdater注意點
- 可見範圍:利用的反射修飾符不能是private
- 不支持static:IllegalArgumentException
7 Adder累加器
- 是Java 8引入的,相對是比較新的一個類
- 高併發下LongAdder比AtomicLong效率高,不過本質是空間換時間
- 競爭激烈的時候,LongAdder把不同線程對應到不同的Cell上進行修改,降低了衝突的概率,是多段鎖的理念,提高了併發性
- 代碼演示
- 這裏演示多線程情況下AtomicLong的性能
- 由於競爭很激烈,每一次加法,都要flush和refresh,導致很耗費資源。
/**
* 演示高併發場景下,LongAdder比AtomicLong性能好
*/
public class AtomicLongDemo {
public static void main(String[] args) throws InterruptedException {
AtomicLong counter = new AtomicLong(0);
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
service.shutdown();
while (!service.isTerminated()) {
}
long end = System.currentTimeMillis();
System.out.println(counter.get());
System.out.println("AtomicLong耗時:" + (end - start));//2229
}
private static class Task implements Runnable {
private AtomicLong counter;
public Task(AtomicLong counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.incrementAndGet();
}
}
}
}
/**
* 演示高併發場景下,LongAdder比AtomicLong性能好
*/
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
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));
}
service.shutdown();
while (!service.isTerminated()) {
}
long end = System.currentTimeMillis();
System.out.println(counter.sum());
System.out.println("LongAdder耗時:" + (end - start));//244
}
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.increment();
}
}
}
}
在內部,這個LongAdder的實現原理和AtomicLong是有不同的,AtomicLong的實現原理是,每一次加法都需要做同步,所以在高併發的時候會導致衝突比較多,也就降低了效率
- LongAdder,每個線程會有自己的一個計數器,僅用來在自己線程內計數,這樣一來就不會和其他線程的計數器干擾
- 如圖中所示,第一個線程的計數器數值,也就是ctr′,爲1的時候,可能線程2的計數器ctr′′的數值已經是3了,他們之間並不存在競爭關係所以在加和的過程中,根本不需要同步機制,也不需要剛纔的flush和refresh。這裏也沒有—個公共的counter來給所有線程統一計數
LongAdder引入了分段累加的概念,內部有一個base變量和一個Cell[]數組共同參與計數
- base變量:競爭不激烈,直接累加到該變量上
- Cell[]數組:競爭激烈,各個線程分散累加到自己的槽Cell[i]中
sum源碼分析
//java.util.concurrent.atomic.LongAdder#sum
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
對比AtomicLong和LongAdder
- 在低爭用下,AtomicLong和LongAdder這兩個類具有相似的特徵。但是在競爭激烈的情況下,LongAdder的預期吞吐量要高得多,但要消耗更多的空間
- LongAdder適合的場景是統計求和計數的場景,而且LongAdder基本只提供了add方法,而AtomicLong還具有cas方法
8 Accumulator累加器
Accumulator和Adder非常相似,Accumulator就是一個更通用版本的Adder
使用場景:大量並行計算,對計算順序不能有要求(不影響最後結果)
/**
* 演示LongAccumulator的用法
*/
public class LongAccumulatorDemo {
public static void main(String[] args) {
//x是傳入的,y是初始值或者上一次計算的值
LongAccumulator accumulator = new LongAccumulator((x, y) -> 2 + x * y, 1);
ExecutorService executor = Executors.newFixedThreadPool(8);
IntStream.range(1, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
}