線程安全性
定義
當(dāng)多個(gè)線程訪問(wèn)某個(gè)類時(shí),不管運(yùn)行時(shí)環(huán)境采取何種調(diào)度方式制市,或者這些線程將如何調(diào)度執(zhí)行抬旺,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個(gè)類都能表現(xiàn)出正確的行為祥楣,那么就稱這個(gè)類是線程安全的开财;
線程安全體現(xiàn)在3個(gè)方面
- 原子性:提供了互斥訪問(wèn)汉柒,同一時(shí)刻只能有一個(gè)線程對(duì)它進(jìn)行操作;
- 可見(jiàn)性:一個(gè)線程對(duì)主內(nèi)存的修改可以及時(shí)的被其它線程觀察到责鳍;
- 有序性:一個(gè)線程觀察其它線程中指令的執(zhí)行順序碾褂,由于指令重排的存在,觀察結(jié)果一般雜亂無(wú)章历葛;
AtomicInteger示例
示例程序描述
- 5000個(gè)線程要去修改count正塌,每個(gè)線程把count加1,同時(shí)并發(fā)執(zhí)行的線程數(shù)為200恤溶;
- 相當(dāng)于5000個(gè)線程擠只有200個(gè)門的“葫蘆口”乓诽,“葫蘆口”的200個(gè)線程還要搶count的修改權(quán);
AtomicInteger要點(diǎn)闡述
- AtomicInteger的實(shí)現(xiàn)是基于CAS(Compare And Swop)原理咒程;
- AtomicInteger的語(yǔ)義是:對(duì)AtomicInteger中維護(hù)的int型變量的操作是原子性的鸠天;
- AtomicInteger實(shí)現(xiàn)的思想是:每個(gè)線程中都會(huì)擁有共享變量的一份私有拷貝,但由于多個(gè)線程都搶著操作共享變量孵坚,當(dāng)前線程的私有拷貝已經(jīng)不是共享變量的最新值粮宛;當(dāng)前線程的私有拷貝只有和共享變量的最新值相等時(shí),才能獲得對(duì)共享變量的操作權(quán)利卖宠,這個(gè)操作是原子性的巍杈;當(dāng)前線程的私有拷貝如何追上共享變量的最新值呢?就是在循環(huán)中不停的和共享變量最新值比扛伍,如果不相等筷畦,就把自己更新成最新值,再比刺洒,不等再更新鳖宾,直到自己和最新值相等才獲得了對(duì)共享變量的操作權(quán);這套規(guī)則是AtomicInteger設(shè)計(jì)的逆航,操作其維護(hù)的int值的線程遵守之鼎文;
示例程序要點(diǎn)闡述
- ExecutorService executorService = Executors.newCachedThreadPool(); 定義了一個(gè)線程池;
- final Semaphore semaphore = new Semaphore(threadTotal); 作為信號(hào)量因俐,限制線程池中并發(fā)執(zhí)行的線程數(shù)拇惋;
- final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); 倒計(jì)時(shí)控制器,每個(gè)線程都會(huì)把控制器的值減1抹剩;
- countDownLatch.await(); 會(huì)一直等到控制器的值減為0再執(zhí)行其后代碼撑帖;
- executorService.shutdown(); 線程池在最后需要關(guān)閉;
import com.example.concurrency.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@ThreadSafe
public class CountExample2 {
// 請(qǐng)求總數(shù)
public static int clientTotal = 5000;
// 同時(shí)并發(fā)執(zhí)行的線程數(shù)
public static int threadTotal = 200;
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet();
// count.getAndIncrement();
}
}
AtomicInteger源碼解析
- unsafe.getAndAddInt(this, valueOffset, 1) 這個(gè)方法執(zhí)行完澳眷,this已經(jīng)加1了胡嘿,但其返回值是共享變量的最新值var5,故返回給調(diào)用處還得再加個(gè)1钳踊;
package java.util.concurrent.atomic;
public class AtomicInteger extends Number implements java.io.Serializable {
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
}
- var1 是線程對(duì)共享變量的私有拷貝衷敌;
- var4 是要給私有拷貝var1加上的值勿侯,但前提條件是:私有拷貝var1已經(jīng)和共享變量的最新值相等了;
- var5 從主內(nèi)存load回來(lái)的共享變量的最新值逢享;
- 如果var1和var5相等了罐监,就把var1更新成var5+var4;
package sun.misc;
public final class Unsafe {
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
}
- var1 是線程對(duì)共享變量的私有拷貝瞒爬;
- var4 共享變量的最新值弓柱;
- var5 var1更新后的值;
- 如果var1 == var4了侧但,就把var1更新成var5矢空;
package sun.misc;
public final class Unsafe {
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
}