多線(xiàn)程基礎(chǔ)之常見(jiàn)概念的理解
我這里從線(xiàn)程的關(guān)鍵概念入手精盅,介紹一下多線(xiàn)程,并且結(jié)合代碼說(shuō)下自己的理解谜酒。
線(xiàn)程安全
我們?cè)趺蠢斫膺@個(gè)線(xiàn)程安全叹俏?什么樣的程序是線(xiàn)程安全的?
首先僻族,關(guān)于線(xiàn)程安全的理解粘驰,當(dāng)程序的返回結(jié)果始終與你預(yù)期的一致時(shí),那這段代碼就是線(xiàn)程安全的述么。
那么什么樣的程序是線(xiàn)程安全的呢晴氨?
就我個(gè)人的理解,多線(xiàn)程安全的程序有幾種情況:
- 程序中不存放共享的數(shù)據(jù)域碉输,多個(gè)線(xiàn)程訪問(wèn)時(shí)根本不會(huì)訪問(wèn)共享的數(shù)據(jù)域籽前,這樣的程序是線(xiàn)程安全的。比如java中的String對(duì)象,它的類(lèi)變量都是final的枝哄,它不可以修改肄梨,就不會(huì)有線(xiàn)程安全的問(wèn)題。
- 在訪問(wèn)共享的數(shù)據(jù)域時(shí)挠锥,進(jìn)入數(shù)據(jù)域時(shí)众羡,先獲取一把監(jiān)視鎖,保證數(shù)據(jù)在同一時(shí)刻只有一個(gè)線(xiàn)程修改蓖租。這里也很好理解粱侣,比如我們家里都有廁所,一個(gè)人上廁所的時(shí)候蓖宦,通常都會(huì)把門(mén)鎖灼胗ぁ;下一個(gè)人來(lái)了稠茂,只能在外面排隊(duì)等待柠偶,里面的人使用完了,這個(gè)人才能接著使用睬关。
大家都知道java中hashmap是線(xiàn)程不安全的诱担,下面例子去說(shuō)明hashmap的線(xiàn)程不安全。
package com.allen.dayup.高并發(fā)程序設(shè)計(jì).chap1;
import java.util.HashMap;
import java.util.Map;
/**
* @Auther: allen
* @Date: 2020-03-20 08:45
* @Description:
*/
public class HashMapMutiThread {
static Map<String,String> map = new HashMap<>();
public static class AddThread extends Thread {
private int start = 0;
public AddThread(String name,int start) {
super(name);
this.start = start;
}
@Override
public void run() {
for (int i = start; i < 100000; i+=2) {
map.put(String.valueOf(i), String.valueOf(i));
}
}
}
public static void main(String[] args) throws InterruptedException {
AddThread addThread = new AddThread("Add_1", 0);
AddThread addThread2 = new AddThread("Add_2", 1);
addThread.start();
addThread2.start();
addThread.join();
addThread2.join();
System.out.println("Map的size:" + map.size());
}
}
程序運(yùn)行完后电爹,可能有兩種結(jié)果(JDK8):
- map的size剛好等于100000蔫仙,程序運(yùn)行正常。
- map的size小于100000丐箩,這是因?yàn)閙ap在擴(kuò)容過(guò)程中摇邦,并沒(méi)有加鎖的,導(dǎo)致另一個(gè)線(xiàn)程成訪問(wèn)到了不一致的內(nèi)部狀態(tài)雏蛮。
可見(jiàn)性和不可見(jiàn)性
討論可見(jiàn)性之前,我們要稍微提一下計(jì)算機(jī)的內(nèi)存模型阱州,計(jì)算機(jī)為了更好發(fā)揮cpu的性能挑秉,內(nèi)部有三級(jí)緩存結(jié)構(gòu)。cpu上的計(jì)算結(jié)果并不是實(shí)時(shí)刷新到主存的苔货。對(duì)于java的普通變量來(lái)說(shuō)犀概,每個(gè)線(xiàn)程都有自己一塊空間存儲(chǔ)變量,一個(gè)線(xiàn)程修改了變量夜惭,并不會(huì)實(shí)時(shí)把變量刷到主存時(shí)姻灶;在變量沒(méi)有刷新的時(shí)間段內(nèi),對(duì)于另外一個(gè)線(xiàn)程诈茧,變量的修改就是不可見(jiàn)的产喉。
在java可以用volatile關(guān)鍵字來(lái)保證變量的可見(jiàn)性。
下面通過(guò)一個(gè)例子來(lái)說(shuō)明線(xiàn)程的可見(jiàn)性。
package com.allen.dayup.高并發(fā)程序設(shè)計(jì).chap1;
/**
* @Auther: allen
* @Date: 2020-03-19 22:15
* @Description:
*/
public class NoVisibility {
//當(dāng)ready為非volatile變量時(shí)曾沈,程序不能正常退出这嚣,因?yàn)閞eader線(xiàn)程不能看到主線(xiàn)程對(duì)變量的修改
private static volatile boolean ready = false;
private static int number;
private static class Reader extends Thread{
@Override
public void run() {
while ( !ready ){
}
System.out.println(number);
}
}
public static void main(String[] args) throws InterruptedException{
Reader reader = new Reader();
reader.start();
number = 42;
ready = true;
}
}
如果ready是非volatile變量時(shí),程序不能正常退出塞俱。