-
原子性
一個具有原子性的操作要么全部執(zhí)行,要么不執(zhí)行忽舟。具體來說双妨,原子性的操作執(zhí)行到一半,并不會因為CPU線程調(diào)度而被打斷叮阅,這樣的操作就是原子性操作刁品。在Java并發(fā)編程中,
synchronized
關(guān)鍵字可以保證操作的原子性浩姥。
public class Atomicity {
//volatile保證有序性和可見性
static volatile int num = 0;
//結(jié)果小于100000
/*public static void incre() {
num++;
}*/
//結(jié)果等于100000
public synchronized static void incre() {
//num++ 等于 num=num+1; 這個操作并不是一個原子操作挑随,它會因為線程調(diào)度而被打斷
//需要家synchronized來保證原子性
num++;
}
public static void main(String[] args) throws InterruptedException {
Thread[] arrThread = new Thread[10];
for(int i = 0; i<arrThread.length; i++) {
arrThread[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j<10000; j++) {
incre();
}
}
});
arrThread[i].start();
}
for(Thread t : arrThread) {
t.join();//等每個線程執(zhí)行完
}
System.out.println("num ="+num);
}
}
-
可見性
程序在多線程下,其中一個線程修改線程之間的共享變量的值時勒叠,其它線程是會感知得道的兜挨,并且會把新的共享變量值以極短的時間同步到其它線程中。在Java中眯分,
volatile
關(guān)鍵字可以保證操作的可見性拌汇。
public class Visibility {
//結(jié)果是 start -> assign -> complete -> 死循環(huán)
//static boolean initFlag = false;
//結(jié)果是 start -> assign -> complete -> end
//加了volatile關(guān)鍵字使得initFlag變量的更改對其它線程可見
static volatile boolean initFlag = false;
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("=========start");
while(!initFlag) {
}
System.out.println("=========end");
}
}).start();
Thread.sleep(2000);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("=========assign");
initFlag = true;
System.out.println("=========complete");
}
}).start();
}
}
-
有序性
程序在多線程下,程序中代碼執(zhí)行的順序是代碼的先后執(zhí)行的弊决。程序代碼不會因為指令重排序(程序為了優(yōu)化執(zhí)行效率從而打亂了代碼的執(zhí)行順序)而打亂噪舀,從而導致程序產(chǎn)生不同的結(jié)果。在Java中同樣飘诗,
volatile
關(guān)鍵字也可以保證操作的有序性与倡。
import java.util.HashMap;
import java.util.HashSet;
public class Ordering {
//運行到后面的結(jié)果:[a=0,b=0, a=1,b=0, a=0,b=1]
//volatile保證了x、y賦值時的先后順序
static volatile int x = 0, y = 0;
//運行到后面的結(jié)果:[a=0,b=0, a=1,b=0, a=0,b=1, a=1,b=1]
//因為指令重排序昆稿,代碼的執(zhí)行順序出現(xiàn)了 2纺座、4操作 在 1、3的前面貌嫡,例如2413,4213
//static int x = 0, y = 0;
public static void main(String[] args) throws InterruptedException {
HashSet<String> hashSet = new HashSet<>();
HashMap<String,Integer> hashMap = new HashMap<>();
for (int i = 0; i<1000000; i++) {
x = 0; y = 0;
hashMap.clear();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
int a = x; //1
y = 1; //2
hashMap.put("a",a);
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
int b = y; //3
x = 1; //4
hashMap.put("b",b);
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
hashSet.add("a="+hashMap.get("a")+",b="+hashMap.get("b"));
System.out.println(hashSet);
}
}
}
-
結(jié)束語
這篇博客只停留在使用層面比驻,其中還有很多原理性的東西该溯,包括Java線程內(nèi)存模型、volatile的底層實現(xiàn)原理别惦、synchronized的底層實現(xiàn)原理狈茉、指令重排等等,這些都沒講掸掸,感興趣的小伙伴可以關(guān)注我以后的文章氯庆。