一、原子性
????原子性操作指相應(yīng)的操作是單一不可分割的操作。在我們學(xué)化學(xué)這門課程的時(shí)候,對于里面講到的原子性相信大家都非常明白,原子是微觀世界中最小的不可再進(jìn)行分割的單元,原子是最小的粒子。java里面的原子性操作也是如此,它代表著一個(gè)操作不能再進(jìn)行分割是最小的執(zhí)行單元,或者一系列操作要么全部成功執(zhí)行,要么全部執(zhí)行失敗,不允許中間某一些成功失敗,類比如事物控制,要么全部提交要么全部回滾。 ????下面根據(jù)幾個(gè)粒子來分析下原子性操作:
i?=?0;???????//1 j?=?i?;??????//2 i++;?????????//3 i?=?j?+?1;???//4
上面四個(gè)操作,有哪個(gè)幾個(gè)是原子操作,那幾個(gè)不是?如果不是很理解,可能會(huì)認(rèn)為都是原子性操作,其實(shí)只有1才是原子操作,其余均不是。
1在Java中,對基本數(shù)據(jù)類型的變量和賦值操作都是原子性操作;? 2中包含了兩個(gè)操作:讀取i,將i值賦值給j? 3中包含了三個(gè)操作:讀取i值、i?+?1?、將+1結(jié)果賦值給i;? 4中同三一樣
在單線程環(huán)境下我們可以認(rèn)為整個(gè)步驟都是原子性操作,但是在多線程環(huán)境下則不同,Java只保證了基本數(shù)據(jù)類型的變量和賦值操作才是原子性的(注:在32位的JDK環(huán)境下,對64位數(shù)據(jù)的讀取不是原子性操作*,如long、double)。在多線程環(huán)境中,非原子操作可能會(huì)受其他線程的干擾,例如第3個(gè)操作,i在加1之后將結(jié)果賦值給i,在賦值給i回寫主內(nèi)存的時(shí)候可能會(huì)被其他線程搶先回寫,導(dǎo)致此次執(zhí)行失敗丟失了本次計(jì)算結(jié)果(這里會(huì)涉及到原子性操作,下面會(huì)進(jìn)行講解)。
public?class?AtomicTest?{
????private?int?i?=?0;
????public?void?add()?{
????????i++;
????}
????public?static?void?main(String[]?args)?{????????
????????for?(int?t?=?0;?t?<?10;?t++)?{
????????????AtomicTest?test?=?new?AtomicTest();
????????????Thread[]?threads?=?new?Thread[10];???????????
????????????for?(int?i?=?0;?i?<?threads.length;?i++)?{
????????????????threads[i]?=?new?Thread(()?->?{????????????????????
????????????????for?(int?k?=?0;?k?<?1000;?k++)?{
????????????????????????test.add();
????????????????????}
????????????????});???????????????
????????????????threads[i].start();
????????????}????????????
????????????Arrays.stream(threads).forEach(th?->?{????????????????
????????????try?{
????????????????????th.join();
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????});????????????
????????????System.out.println("第"?+?(t?+?1)?+?"次執(zhí)行結(jié)果:"?+?test.i);
????????}
????}
}第1次執(zhí)行結(jié)果:8987第2次執(zhí)行結(jié)果:8970第3次執(zhí)行結(jié)果:6820第4次執(zhí)行結(jié)果:9841第5次執(zhí)行結(jié)果:10000第6次執(zhí)行結(jié)果:7766第7次執(zhí)行結(jié)果:8105第8次執(zhí)行結(jié)果:10000第9次執(zhí)行結(jié)果:10000第10次執(zhí)行結(jié)果:10000
最終的執(zhí)行結(jié)果會(huì)是小于等于10000,在某些情況下與我們所期望的結(jié)果10000不符合,并發(fā)的情況下導(dǎo)致bug的產(chǎn)生。
要想在多線程環(huán)境下保證原子性,則可以通過鎖、synchronized來確保。volatile是無法保證復(fù)合操作的原子性。
二、可見性
int?i?=?0;????????????//語句1?? boolean?flag?=?false;?//語句2 i?=?1;????????????????//語句3?? flag?=?true;??????????//語句4
public?class?SerialTest?{ ????static?SerialTest?serialTest; ????static?boolean?isInit?=?false; ????public?static?void?main(String[]?args)?{???????? ????for(int?i=0;?i<?200;i++)?{ ????????????serialTest?=?null; ????????????isInit?=?false;???????????? ????????????new?Thread(()->{ ????????????????serialTest?=?new?SerialTest();//語句1 ????????????????isInit?=?true;????????????????//語句2 ????????????}).start();???????????? ????????????new?Thread(()->{???????????????? ????????????????if(isInit)?{ ????????????????????serialTest.doSomething(); ????????????????} ????????????}).start(); ????????} ????}???? ????public?void?doSomething()?{???????? ????????System.out.println("doSomething"); ????} }
Exception?in?thread?"Thread-283"?java.lang.NullPointerException at?com.cd.concurrent.SerialTest.lambda$main$1(SerialTest.java:25) at?java.lang.Thread.run(Thread.java:748) ...... doSomething doSomething doSomething doSomething doSomething Exception?in?thread?"Thread-283"?java.lang.NullPointerException at?com.cd.concurrent.SerialTest.lambda$main$1(SerialTest.java:25) at?java.lang.Thread.run(Thread.java:748)
