C語言中文網 目錄

Java多線程之間訪問實例變量

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

圖 1 所示為不共享數據的示例,圖 2 所示為共享數據的示例。

線程之間不共享數據實例圖
圖1 線程之間不共享數據實例圖

線程間共享數據示例圖
圖2 線程間共享數據示例圖

例 1

如圖 1 所示,在不共享數據時每個線程都擁有自己作用域的變量,且多個線程之間相同變量名的值也不相同。下面創建一個示例演示這種特性。

首先創建自定義的線程類 MyThread03, 代碼如下:
package ch14;
public class MyThread03  extends Thread
{
    private int count=5; 
    public MyThread03(String name)
    { 
        super(); 
        this.setName(name);//設置線程名稱 
    } 
    @Override 
    public void run()
    { 
        super.run(); 
        while (count>0)
        { 
            count--; 
            System.out.println("由 "+this.currentThread().getName()+" 計算,count="+count); 
        } 
    }
}

如上述代碼所示,MyThread03 線程的代碼非常簡單。下面編寫代碼在主線程中創建 3 個 MyThread03 線程,并啟動這些線程。具體代碼如下:
package ch14;
public class Test05
{
    public static void main(String[] args)
    { 
        MyThread03 a=new MyThread03("A"); 
        MyThread03 b=new MyThread03("B"); 
        MyThread03 c=new MyThread03("C"); 
        a.start(); 
        b.start(); 
        c.start(); 
    }
}

運行主線程將看到圖 1 所示效果。從如下所示的運行結果可以看出,程序一共創建了 3 個線程,每個線程都有各自的 count 變量,自己減少自己的 count 變量的值。這樣的情況就是變量不共享,此實例并不存在多個線程訪問同一個實例變量的情況。
由 B 計算,count=4
由 B 計算,count=3
由 B 計算,count=2
由 B 計算,count=1
由 B 計算,count=0
由 C 計算,count=4
由 C 計算,count=3
由 C 計算,count=2
由 C 計算,count=1
由 C 計算,count=0
由 A 計算,count=4
由 A 計算,count=3
由 A 計算,count=2
由 A 計算,count=1
由 A 計算,count=0

例 2

如果想實現多個線程共同對一個變量進行操作的目的,該如何設計代碼呢?這時就必須使用共享數據的方案。共享數據的情況就是多個線程可以訪問同一個變量,比如在實現投票功能的軟件時,多個線程可以同時處理同一個人的票數。

下面通過一個示例來看一下數據共享情況。首先對例 1 的 MyThread03 類進行修改,這里將新線程類命名為 MyThread04。
package ch14;
public class MyThread04  extends Thread
{
    private int count=5; 
    @Override 
    public void run()
    { 
        super.run(); 
        count--; 
        //此示例不要用for語句,因為使用同步后其他線程就得不到運行的機會了, 
        //一直由一個線程進行減法運算 
        System.out.println("由 "+this.currentThread().getName()+" 計算,count="+count); 
    }
}

編寫代碼在主線程中創建 5 個 MyThread04 線程,并啟動這些線程。具體代碼如下:
package ch14;
public class Test06
{
    public static void main(String[] args)
    { 
        MyThread04 mythread=new MyThread04(); 
        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();
    }
}

運行主線程將看到如下所示的效果。從運行結果中可以看到,線程 A 和 B 打印出的 count 值都是 3,說明 A 和 B 同時對 count 進行處理,產生了“非線程安全”問題,但我們想要得到的打印結果卻不是重復的,而是依次遞減的。
由 A 計算,count=4
由 B 計算,count=3
由 C 計算,count=1
由 E 計算,count=1
由 D 計算,count=0

在某些 JVM 中,i-- 的操作要分成如下3步:
  • 取得原有 i 值。
  • 計算 i-1。
  • 對 i 進行賦值。

在這 3 個步驟中,如果有多個線程同時訪問,那么一定會出現非線程安全問題。

其實這個示例就是典型的銷售場景:5 名銷售員,每名銷售員賣出一件貨品后不可以得出相同的剩余數量,必須在每一名銷售員賣完一件貨品后其他銷售員才可以在新的剩余物品數上繼續減1操作。這時就需要使多個線程之間進行同步,也就是用按順序排隊的方式進行減1操作。更改代碼如下:
package ch14;
public class MyThread04  extends Thread
{
    private int count=5; 
    @Override 
    synchronized  public void run()
    { 
        super.run(); 
        count--; 
        //此示例不要用for語句,因為使用同步后其他線程就得不到運行的機會了, 
        //一直由一個線程進行減法運算 
        System.out.println("由 "+this.currentThread().getName()+" 計算,count="+count); 
    }
}

再次運行程序,就不會出現值一樣的情況了,如下所示。
由 A 計算,count=4
由 B 計算,count=3
由 C 計算,count=2
由 D 計算,count=1
由 E 計算,count=0

通過在 run() 方法前加 synchronized 關鍵字,使多個線程在執行 run() 方法時,以排隊的方式進行處理。當一個線程調用 run() 前,先判斷 run() 方法有沒有被上鎖,如果上鎖,說明有其他線程正在調用 run()方法,必須等其他線程對 run() 方法調用結束后才可以執行 run()方法。這樣也就實現了排隊調用 run() 方法的目的,達到了按順序對 count 變量減 1 的效果。synchronized 可以在任意對象及方法上加鎖,而加鎖的這段代碼稱為“互斥區” 或“臨界區”。

當一個線程想要執行同步方法里面的代碼時,線程首先嘗試去拿這把鎖,如果能夠拿到鎖,那么這個線程就可以執行 synchronize 里面的代碼。如果不能拿到鎖,那么這個線程就會不斷地嘗試拿鎖,直到能夠拿到為止,而且有多個線程同時去爭搶這把鎖。

精美而實用的網站,提供C語言、C++、STL、Linux、Shell、Java、Go語言等教程,以及socket、GCC、vi、Swing、設計模式、JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo
极速pk10开户