多線程對共享變量的訪問,通過瑣保證互斥訪問。本章主要討論如何在多線程間共享對象,保證其被安全訪問。在編寫多線程程序時(shí),最重要的就是搞清楚哪些變量是共享的,哪些變量是不共享的。也就是要分析清楚其中的原理呀。
實(shí)現(xiàn)線程安全的方法之一是不在線程間共享變量,將變量的使用范圍限制在單個線程之內(nèi),即實(shí)現(xiàn)Thread Confinement。
因?yàn)樽罱褂枚嗑程就看了一些,對使用Thread類的子類創(chuàng)建線程的情況,總結(jié)如下:
1.方法體內(nèi)部定義的局部變量不共享
這是因?yàn)榉椒▋?nèi)部定義的變量是在運(yùn)行時(shí)動態(tài)生成的。每個線程都有一個自己的堆棧,用于保存運(yùn)行時(shí)的數(shù)據(jù)。
最容易理解的就是遞歸調(diào)用時(shí)候,每次的入棧出棧操作。如下,每次調(diào)用時(shí),變量aa都是在運(yùn)行時(shí)堆棧上保存的,方法結(jié)束變量也就釋放了。
public int fib(int n) { int aa; if(n==1 || n==0) return 1; else return fib(n-1)*n; }
2.成員變量
2.1 代碼示例
成員變量需要看變量指向的是否為同一個對象?聪旅娴拇a示例:
package file2; public class Analy { public static void main(String[] args) { Num i=new Num(0); //新建對象,準(zhǔn)備傳遞給線程 new OwnThread(i).start(); //新建線程,并啟動 new OwnThread(i).start(); //新建線程,并啟動 System.out.println("主線程中i的值變?yōu)榱耍?quot;+i.i); //獲取目前對象i的數(shù)值 } } class OwnThread extends Thread { Num id; //申明對象,默認(rèn)null,就是沒有指向任何實(shí)體 int sno; //申明int變量。因?yàn)橄到y(tǒng)默認(rèn)初始化為0,所以應(yīng)該是定義一個int變量 OwnThread(Num id) { this.id=id; } public void run() { for(int i=0;i<5;i++) { synchronized(this) { sno=id.i; //保存id.i的數(shù)值,到線程私有變量sno id.i++; try { Thread.sleep(1); } catch (InterruptedException e) {} } System.out.println(this.getName()+","+sno); } } } class Num //定義一個類 { int i; Num(int i) { this.i=i; } }
共享同一個對象,線程可以交互,執(zhí)行結(jié)果:
2.2分析
程序中主函數(shù)定義了Num對象的實(shí)例i,定義線程是傳遞到了Thread0和Thread1這樣三個變量就共享了一個Num對象的實(shí)例。而線程Thread0和線程Thread1又有自己的私有變量sno,可以用來保存某一時(shí)刻的共享變量的數(shù)值。
注意:
(1)Java中判斷對象是否為同一個對象使用地址判斷的。地址相同就是同一個對象,上面的三個就是同一個對象。
(2)如果把上面的例子中共享的對象實(shí)例用基本數(shù)據(jù)類型替換是不行的。因?yàn)榛緮?shù)據(jù)類型程序會自動的用默認(rèn)值初始化,也就是申明和定義時(shí)一起的。此時(shí)在mian函數(shù)中定義線程,傳遞的基本數(shù)據(jù)類型參數(shù),只能是初始化線程中的另一個對象,而不是同一個對象。
3.總結(jié)
總之,在多線程編程中,知道各個線程如何、怎么樣共享數(shù)據(jù)是很重要的。如上面的程序,可以在主線程和其他兩個子線程之間共享一個對象,來實(shí)現(xiàn)他們之間的交互處理。