今天看了個java面試題,提到了線程安全與不安全的問題,腦子中沒有一點概念,剛好今天有時間,就把這個問題理清楚,分享出來,讓更多和我一樣的小白瞭解下這個知識點。每天進步一點點哦~
1、線程到底是什麼呢?
進程是資源調度的最小單位,線程是cpu調度的最小單位。
所有與進程有關的資源都被記錄在pcb中(進程控制塊)
(1)進程
每個進程獨佔內存空間,保存各自運行狀態,相互間不干擾且可以互相切換,爲併發處理任務提供可能性。
每天玩電腦的人,大家應該都清楚,進程是什麼,鼠標右擊任務欄,點擊任務管理器的第一個sheet頁,展示了電腦目前運行的所有進程。
其實每個單獨的程序就是一個進程。
liunx也一樣,啓動一個tomcat就是一個進程。
(2)線程與多線程
進程若要執行命令就必須依賴於線程,也就是說,一個進程必須至少有一個線程。而線程和多線程的區別,就是比如下載遊戲,下載文檔,如果是一個線程進行的話,就需要串行順序,按照順序進行下載。而並行就相當於你開了三個窗口同時進行下載,並行就是多線程。也就是一個進程運行產生的多個線程。
例:
2、怎樣理解線程安全呢?
百度百科這樣解釋:
線程安全是多線程編程時的計算機程序代碼中的一個概念。
在擁有共享數據的多條線程並行執行的程序中,線程安全的代碼會通過同步機制保證各個線程都可以正常且正確的執行,不會出現數據污染等意外情況。
簡單的用ArrayList舉個反例,由於ArrayList就是線程不安全的。
代碼如下:
public static void main(String[] args) throws InterruptedException {
final List<Integer> list = new ArrayList<>();
for (int a = 0;a <3 ; a++) {
// 線程A將0-1000添加到list
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 1000 ; i++) {
list.add(i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
Thread.sleep(1000*30);
// 打印所有結果
System.out.println("共有" + list.size()+"個元素。");
for (int i = 0; i < list.size(); i++) {
if(list.get(i)==null){
System.out.println("第" + (i + 1) + "個元素爲:null");
}
}
}
運行,可能出現的執行結果如下:
而是用線程安全的Vector類運行,即
List<Integer> list = new Vector<>();
運行後結果如下:
由此可以說明,使用ArrayList插入數字時,可能出現下標越界或者插入數據爲null,由此可以說明,arraylist是線程不安全的。
再舉個簡單的例子:
public static void main(String[] args){
for (int i = 0; i <3 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(getcountNum(30));
}
}).start();
}
}
public static int getcountNum(int j){
int i =1;
j=j+3;
return j;
}
這個線程安全嗎?
答案是肯定的,因爲這段代碼是沒有任何狀態的,也就是說,這段代碼不包含任何作用域,也沒有取引用其他類中的域,他所執行的結果只存在在它當前運行的這條賢臣的局部變量中,並只由當前線程去訪問,不會影響到其他線程。
多條線程同時訪問時,無共享數據,所以線程是安全的。
那如果添加一個共享變量呢,結果如何?
public static void main(String[] args){
for (int i = 0; i <3 ; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(getcountNum(30));
System.out.println("count爲:"+count);
try{
Thread.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}
public static int getcountNum(int j){
int i =1;
count++;
j=j+3;
return j;
}
結果:
由此可見不同的線程,訪問到的count數據和預想的也不一致。
這就導致了線程的不安全。
後續會繼續解釋,如何實現線程安全。