多線程-實例變量與線程安全

   自定義線程類中的實例變量針對其他線程有共享和不共享之分,這在多個線程之間進行交互時是一個很重要的技術點。
  1. 不共享數據的情況

    下面通過一個示例來看下數據不共享的情況。

package com.vhqimk.thread;

/*
 * 測試數據不共享的情況
 */
public class Test {
    public static void main(String[] args) {
        // 創建三個MyThread對象,並分別給線程命名
        MyThread a = new MyThread("A");
        MyThread b = new MyThread("B");
        MyThread c = new MyThread("C");
        // 啓動三個線程
        a.start();
        b.start();
        c.start();
    }
}

class MyThread extends Thread {
    private int count = 5;

    public MyThread(String name) {
        super();
        this.setName(name);// 設置線程名稱
    }

    public void run() {
        super.run();
        while (count > 0) {
            count--;
            System.out.println("由" + Thread.currentThread().getName() + " 計算,count=" + count);
        }
    }
}

不共享數據運行結果如圖所示。
不共享數據運行結果
圖 1-1 不共享數據的運行結果
由圖 1-1可以看到,一共創建了三個線程,每個線程都有各自的count變量,自己減少自己的count變量的值。這樣的情況就是變量不共享,此示例不存在多個線程訪問同一個實例變量的情況。
2. 共享數據的情況
共享數據的情況就是多個線程可以訪問同一個變量,比如在實現投票功能的軟件時,多個線程可以同時處理一個人的票數。
下面通過一個示例來看下數據共享的情況。

package com.vhqimk.thread;

/*
 * 測試數據共享的情況
 */
public class Test {
    public static void main(String[] args) {
        // 創建一個MyThread對象,並將該對象分別加載到五個線程中並分別給線程命名
        MyThread myThread = new MyThread();
        Thread a = new Thread(myThread, "A");
        Thread b = new Thread(myThread, "B");
        Thread c = new Thread(myThread, "C");
        Thread d = new Thread(myThread, "D");
        Thread e = new Thread(myThread, "E");

        // 啓動五個線程
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}

class MyThread extends Thread {
    private int count = 5;

    public void run() {
        super.run();
        count--;
        System.out.println("由" + Thread.currentThread().getName() + " 計算,count=" + count);
    }
}

運行結果如圖 1-2所示。
共享數據運行結果
圖 1-2 共享數據運行結果
從圖 1-2中可以看到線程A和線程B打印出的count值都是3,說明A和B同時對count進行處理,產生了“非線程安全”問題,而我們想要的結果卻不是重複的,而是依次遞減的。
非線程安全主要是指多個線程對同一個對象中的同一個實例變量進行操作時會出現值被更改、值不同步的情況。若要解決,只需在需要線程同步執行的方法前加synchronized。下面是在方法前加上synchronized關鍵字解決非線程安全的問題示例。

package com.vhqimk.thread;

/*
 * 測試數據共享的情況
 */
public class Test {
    public static void main(String[] args) {
        // 創建一個MyThread對象,並將該對象分別加載到五個線程中並分別給線程命名
        MyThread myThread = new MyThread();
        Thread a = new Thread(myThread, "A");
        Thread b = new Thread(myThread, "B");
        Thread c = new Thread(myThread, "C");
        Thread d = new Thread(myThread, "D");
        Thread e = new Thread(myThread, "E");

        // 啓動五個線程
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}

class MyThread extends Thread {
    private int count = 5;
    //在方法前加上synchronized,使得方法裏的線程被同步執行
    synchronized public void run() {
        super.run();
        count--;
        System.out.println("由" + Thread.currentThread().getName() + " 計算,count=" + count);
    }
}

線程同步運行結果如圖 1-3所示。
這裏寫圖片描述

圖 1-3 方法調用被同步

總結:線程有共享數據和不共享數據之分,共享數據時容易出現“非線程安全”問題,可以用關鍵字synchronized 解決。synchronized 可以在任意對象和方法上加鎖,而加鎖的這段代碼稱爲“互斥區”或“臨界區”。當一個線程想要執行同步方法裏的代碼時,縣城首先嚐試去拿這把鎖,如果能夠拿到這把鎖,那麼這個線程就可以執行synchronized 裏面的代碼。如果不能拿到這把鎖,那麼這個線程就會不斷嘗試拿這把鎖,直到能夠拿到爲止,而且是有多個線程同時去爭搶這把鎖。

發佈了28 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章