[摘要] 本文介紹了線程及其運(yùn)行機(jī)理,探討了Java線程的設(shè)計(jì)方法,并就Java線程設(shè)計(jì)中的關(guān)鍵問(wèn)題即同步線程的實(shí)現(xiàn)問(wèn)題提出了四種解決方案。
[關(guān)鍵詞] 線程 多線程 同步
一、線程相關(guān)概念
所謂“線程”(Thread),是“進(jìn)程”中某個(gè)單一順序的控制流。而多線程是這樣一種機(jī)制,它允許在程序中并發(fā)執(zhí)行多個(gè)指令流,每個(gè)指令流都稱(chēng)為一個(gè)線程,彼此間互相獨(dú)立。線程又稱(chēng)為輕量級(jí)進(jìn)程,它和進(jìn)程一樣擁有獨(dú)立的執(zhí)行控制,由操作系統(tǒng)負(fù)責(zé)調(diào)度,區(qū)別在于線程沒(méi)有獨(dú)立的存儲(chǔ)空間,而是和所屬進(jìn)程中的其它線程共享一個(gè)存儲(chǔ)空間,這使得線程間的通信較進(jìn)程簡(jiǎn)單。
多個(gè)線程的執(zhí)行是并發(fā)的,也就是在邏輯上“同時(shí)”,而不管是否是物理上的“同時(shí)”。如果系統(tǒng)只有一個(gè)CPU,那么真正的“同時(shí)”是不可能的,但是由于CPU的速度非??欤脩舾杏X(jué)不到其中的區(qū)別,因此我們也不用關(guān)心它,只需要設(shè)想各個(gè)線程是同時(shí)執(zhí)行即可。
多線程和傳統(tǒng)的單線程在程序設(shè)計(jì)上最大的區(qū)別在于,由于各個(gè)線程的控制流彼此獨(dú)立,使得各個(gè)線程之間的代碼是亂序執(zhí)行的,由此帶來(lái)的線程調(diào)度、同步等問(wèn)題,接下來(lái)我們就對(duì)Java中對(duì)線程設(shè)計(jì)與同步問(wèn)題進(jìn)行探討。
二、線程的設(shè)計(jì)方法
創(chuàng)建線程主要有兩種形式:一種是繼承自Thread類(lèi)。要?jiǎng)?chuàng)建一個(gè)線程,必須創(chuàng)建一個(gè)從 Thread 類(lèi)導(dǎo)出的新類(lèi)。通過(guò)覆蓋 Thread的run()函數(shù)來(lái)完成有用的工作。但用戶通過(guò)調(diào)用Thread的start() 函數(shù)調(diào)用run()。
例:
public class Ctest extends Thread
{
public Ctest()
{…}
public static void main(String args[])
{Ctest t1=new Ctest();
t1.start();}
public void run()
{….}
}
另一種是實(shí)現(xiàn)Runnable接口,此接口只有一個(gè)函數(shù)run(),它由實(shí)現(xiàn)此接口的類(lèi)實(shí)現(xiàn)。
例:
public class Ctest implements Runnable
{ Thread thread1;
public Ctest()
{ thread1=new Thread(this,\"1\");}
public static void main(String args[])
{Ctest t=new Ctest();
t.startThreads(); }
public void run()
{…}
public void startThreads()
{ thread1.start(); }
}
兩種創(chuàng)建方式差別不大,第一種因?yàn)槔^承自Thread,只創(chuàng)建了自身對(duì)象;第二種還得創(chuàng)建Thread對(duì)象。但是當(dāng)你想繼承某一其他類(lèi)時(shí),只能用后一種方式。
三、同步線程實(shí)現(xiàn)
線程的創(chuàng)建雖然只有兩種形式,但是利用這兩種形式我們可以在同一進(jìn)程中設(shè)計(jì)多個(gè)線程;多個(gè)線程并發(fā)執(zhí)行,極大地提高的程序的運(yùn)行效率。但是許多線程在執(zhí)行中必須考慮與其他線程之間共享數(shù)據(jù)或協(xié)調(diào)執(zhí)行狀態(tài),否則會(huì)出現(xiàn)沖突。
解決沖突的很好辦法就是在設(shè)計(jì)中引入同步機(jī)制。在Java中每個(gè)對(duì)象都有一把鎖與之對(duì)應(yīng)。但Java不提供單獨(dú)的lock和unlock操作。它由高層的結(jié)構(gòu)隱式實(shí)現(xiàn),,保證操作的對(duì)應(yīng)。
synchronized語(yǔ)句計(jì)算一個(gè)對(duì)象引用,試圖對(duì)該對(duì)象完成鎖操作,并在完成鎖操作前停止處理。當(dāng)鎖操作完成synchronized語(yǔ)句體得到執(zhí)行;當(dāng)語(yǔ)句體執(zhí)行完畢(無(wú)論正?;虍惓?,解鎖操作自動(dòng)完成。作為面向?qū)ο蟮恼Z(yǔ)言,synchronized經(jīng)常與方法連用。一種比較好的辦法是,如果某個(gè)變量由一個(gè)線程賦值并由別的線程引用或賦值,那么所有對(duì)該變量的訪問(wèn)都必須在某個(gè)synchronized語(yǔ)句或方法內(nèi),具體方法如下:
(1)方法聲明時(shí)使用,放在范圍操作符之后,返回類(lèi)型聲明之前。
這時(shí)線程獲得的是成員鎖,即一次只能有一個(gè)線程進(jìn)入該方法,其他線程要想在此時(shí)調(diào)用該方法,只能排隊(duì)等候,當(dāng)前線程執(zhí)行完該方法后,別的線程才能進(jìn)入。
例:public synchronized void synMethod()
(2)對(duì)某一代碼塊使用,synchronized后跟括號(hào),括號(hào)里是變量,一次只有一個(gè)線程進(jìn)入該代碼塊。此時(shí),線程獲得的是成員鎖。例:
public int synMethod(int a1)
{synchronized(a1)
}
(3)synchronized后面括號(hào)里是一對(duì)象,此時(shí)線程獲得的是對(duì)象鎖。
例:
public class MyThread implements Runnable
{public static void main(String args[])
{MyThread mt=new MyThread();
Thread t2=new Thread(mt, \"t2\");
t2.start();
}
public void run()
{synchronized (this)
{System.out.println(Thread.currentThread().getName());}
}}
對(duì)于(3),如果線程進(jìn)入,則得到當(dāng)前對(duì)象鎖,那么別的線程在該類(lèi)所有對(duì)象上的任何操作都不能進(jìn)行。
(4)synchronized后面括號(hào)里是類(lèi),此時(shí),線程獲得的是對(duì)象鎖,例:
class ArrayWithLockOrder{
private static long num_locks=0;
private long lock_order;
private int[] arr;
public ArrayWithLockOrder(int[] a)
{arr=a;
synchronized(ArrayWithLockOrder.class){
num_locks++;
lock_order=num_locks;
}
}
public long lockOrder()
{return lock_order;}
public int[] array()
{return arr;}
}
class SomeClass implements Runnable
{public int sumArrays(ArrayWithLockOrder a1,ArrayWithLockOrder a2)
{int value = 0;
ArrayWithLockOrder first = a1;
ArrayWithLockOrder last = a2;
int size=a1.array().length;
if (size==a2.array().length)
{
if (a1.lockOrder()>a2.lockOrder())
{first=a2;
last=a1;}
synchronized(first){
synchronized(last){
int[] arr1=a1.array();
int[] arr2=a2.array();
for(int i=0;i value+=arr1[i]+arr2[i]; }}} return value; } public void run() {...} } 對(duì)于(4),如果線程進(jìn)入,則線程在該類(lèi)中所有操作不能進(jìn)行,包括靜態(tài)變量和靜態(tài)方法。對(duì)于含有靜態(tài)方法和變量的代碼塊的同步,常用(4)來(lái)加鎖。 四、小結(jié) 多線程機(jī)制可以同時(shí)運(yùn)行好幾個(gè)程序塊,使程序運(yùn)行的效率變得更高,也可克服傳統(tǒng)程序語(yǔ)言無(wú)法設(shè)計(jì)的問(wèn)題,其設(shè)計(jì)的關(guān)鍵在于同步線程的實(shí)現(xiàn)。除此之外,還需檢查所有可能Block的地方,盡可能多的使用sleep或yield( )以及wait( ),盡可能延長(zhǎng)sleep的時(shí)間,運(yùn)行的線程不用超過(guò)100個(gè),使多線程真正服務(wù)于程序員。 本文中所涉及到的圖表、注解、公式等內(nèi)容請(qǐng)以PDF格式閱讀原文。