1、原子操作類AtomicInteger
? ??對于Java中的運(yùn)算操作,例如自增或自減,若沒有進(jìn)行額外的同步操作,在多線程環(huán)境下就是線程不安全的。num++解析為num=num+1,明顯,這個操作不具備原子性,多線程并發(fā)共享這個變量時必然會出現(xiàn)問題,測試代碼如下:
public class AtomicIntegerTest {
? ? private static final int THREADS_CONUT = 20;
? ? public static int count = 0;
? ? public static void increase() {
? ? ? ? count++;
? ? }
? ? public static void main(String[] args) {
? ? ? ? Thread[] threads = new Thread[THREADS_CONUT];
? ? ? ? for (int i = 0; i < THREADS_CONUT; i++) {
? ? ? ? ? ? threads[i] = new Thread(new Runnable() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 1000; i++) {
? ? ? ? ? ? ? ? ? ? ? ? increase();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? threads[i].start();
? ? ? ? }
? ? ? ? while (Thread.activeCount() > 1) {
? ? ? ? ? ? Thread.yield();
? ? ? ? }
? ? ? ? System.out.println(count);
? ? }
}
------------------------------------------------------------------------------------------------------
這里運(yùn)行了20個線程,每個線程對count變量進(jìn)行1000此自增操作,如果上面這段代碼能夠正常并發(fā)的話,最后的結(jié)果應(yīng)該是20000才對,但實際結(jié)果卻發(fā)現(xiàn)每次運(yùn)行的結(jié)果都不相同,都是一個小于20000的數(shù)字。這是為什么呢?
要是換成volatile修飾count變量呢?
順帶說下volatile關(guān)鍵字很重要的兩個特性:
1、保證變量在線程間可見,對volatile變量所有的寫操作都能立即反應(yīng)到其他線程中,換句話說,volatile變量在各個線程中是一致的(得益于java內(nèi)存模型—"先行發(fā)生原則");
2、禁止指令的重排序優(yōu)化;
那么換成volatile修飾count變量后,會有什么效果呢? 試一試:
public class AtomicIntegerTest {
? ? private static final int THREADS_CONUT = 20;
? ? public static volatile int count = 0;
? ? public static void increase() {
? ? ? ? count++;
? ? }
? ? public static void main(String[] args) {
? ? ? ? Thread[] threads = new Thread[THREADS_CONUT];
? ? ? ? for (int i = 0; i < THREADS_CONUT; i++) {
? ? ? ? ? ? threads[i] = new Thread(new Runnable() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 1000; i++) {
? ? ? ? ? ? ? ? ? ? ? ? increase();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? threads[i].start();
? ? ? ? }
? ? ? ? while (Thread.activeCount() > 1) {
? ? ? ? ? ? Thread.yield();
? ? ? ? }
? ? ? ? System.out.println(count);
? ? }
}
---------------------
結(jié)果似乎又失望了,測試結(jié)果和上面的一致,每次都是輸出小于20000的數(shù)字。這又是為什么么? 上面的論據(jù)是正確的,也就是上面標(biāo)紅的內(nèi)容,但是這個論據(jù)并不能得出"基于volatile變量的運(yùn)算在并發(fā)下是安全的"這個結(jié)論,因為核心點(diǎn)在于java里的運(yùn)算(比如自增)并不是原子性的。
用了AtomicInteger類后會變成什么樣子呢?
把上面的代碼改造成AtomicInteger原子類型,先看看效果
public class AtomicIntegerTest {
? ? private static final int THREADS_CONUT = 20;
? ? public static AtomicInteger count = new AtomicInteger(0);
? ? public static void increase() {
? ? ? ? count.incrementAndGet();
? ? }
? ? public static void main(String[] args) {
? ? ? ? Thread[] threads = new Thread[THREADS_CONUT];
? ? ? ? for (int i = 0; i < THREADS_CONUT; i++) {
? ? ? ? ? ? threads[i] = new Thread(new Runnable() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < 1000; i++) {
? ? ? ? ? ? ? ? ? ? ? ? increase();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? threads[i].start();
? ? ? ? }
? ? ? ? while (Thread.activeCount() > 1) {
? ? ? ? ? ? Thread.yield();
? ? ? ? }
? ? ? ? System.out.println(count);
? ? }
}
---------------------
結(jié)果每次都輸出20000,程序輸出了正確的結(jié)果,這都歸功于AtomicInteger.incrementAndGet()方法的原子性。