在Java語言產生前傳統的程序設計語言的程序同一時刻只能單任務操作效率非常低例如程序往往在接收數據輸入時發生阻塞只有等到程序獲得數據後才能繼續運行 隨著Internet的迅猛發展這種狀況越來越不能讓人們忍受如果網絡接收數據阻塞後台程序就處於等待狀態而不繼續任何操作而這種阻塞是經常會碰到的此時CPU資源被白白的閒置起來如果在後台程序中能夠同時處理多個任務該多好啊!應Internet技術而生的Java語言解決了這個問題多線程程序是Java語言的一個很重要的特點在一個Java程序中我們可以同時並行運行多個相對獨立的線程例如我們如果創建一個線程來進行數據輸入輸出而創建另一個線程在後台進行其它的數據處理如果輸入輸出線程在接收數據時阻塞而處理數據的線程仍然在運行多線程程序設計大大提高了程序執行效率和處理能力
線程的創建
我們知道Java是面向對象的程序語言用Java進行程序設計就是設計和使用類Java為我們提供了線程類Thread來創建線程創建線程與創建普通的類的對象的操作是一樣的而線程就是Thread類或其子類的實例對象下面是一個創建啟動一個線程的語句
Thread thread=new Thread(); file://聲明一個對象實例即創建一個線程
Threadrun(); file://用Thread類中的run()方法啟動線程
從這個例子我們可以通過Thread()構造方法創建一個線程並啟動該線程事實上啟動線程也就是啟動線程的run()方法而Thread類中的run()方法沒有任何操作語句所以這個線程沒有任何操作要使線程實現預定功能必須定義自己的run()方法Java中通常有兩種方式定義run()方法
通過定義一個Thread類的子類在該子類中重寫run()方法Thread子類的實例對象就是一個線程顯然該線程有我們自己設計的線程體run()方法啟動線程就啟動了子類中重寫的run()方法
通過Runnable接口在該接口中定義run()方法的接口所謂接口跟類非常類似主要用來實現特殊功能如復雜關系的多重繼承功能在此我們定義一個實現Runnable() 接口的類在該類中定義自己的run()方法然後以該類的實例對象為參數調用Thread類的構造方法來創建一個線程
線程被實際創建後處於待命狀態激活(啟動)線程就是啟動線程的run()方法這是通過調用線程的start()方法來實現的
下面一個例子實踐了如何通過上述兩種方法創建線程並啟動它們
// 通過Thread類的子類創建的線程
class thread extends Thread
{
file://自定義線程的run()方法
public void run()
{
Systemoutprintln(Thread is running…);
}
}
file://通過Runnable接口創建的另外一個線程
class thread implements Runnable
{
file://自定義線程的run()方法
public void run()
{
Systemoutprintln(Thread is running…);
}
}
file://程序的主類
class Multi_Thread file://聲明主類
{
plubic static void mail(String args[]) file://聲明主方法
{
thread threadone=new thread(); file://用Thread類的子類創建線程
Thread threadtwo=new Thread(new thread()); file://用Runnable接口類的對象創建線程
threadonestart(); threadtwostart(); file://strat()方法啟動線程
}
}
運行該程序就可以看出線程threadone和threadtwo交替占用CPU處於並行運行狀態可以看出啟動線程的run()方法是通過調用線程的start()方法來實現的(見上例中主類)調用start()方法啟動線程的run()方法不同於一般的調用方法調用一般方法時必須等到一般方法執行完畢才能夠返回start()方法而啟動線程的run()方法後start()告訴系統該線程准備就緒可以啟動run()方法後就返回start()方法執行調用start()方法語句下面的語句這時run()方法可能還在運行這樣線程的啟動和運行並行進行實現了多任務操作
線程的優先級
對於多線程程序每個線程的重要程度是不盡相同如多個線程在等待獲得CPU時間時往往我們需要優先級高的線程優先搶占到CPU時間得以執行又如多個線程交替執行時優先級決定了級別高的線程得到CPU的次數多一些且時間多長一些這樣高優先級的線程處理的任務效率就高一些
Java中線程的優先級從低到高以整數~表示共分為級設置優先級是通過調用線程對象的setPriority()方法如上例中設置優先級的語句為
thread threadone=new thread(); file://用Thread類的子類創建線程
Thread threadtwo=new Thread(new thread()); file://用Runnable接口類的對象創建線程
threadonesetPriority(); file://設置threadone的優先級
threadtwosetPriority(); file://設置threadtwo的優先級
threadonestart(); threadtwostart(); file://strat()方法啟動線程
這樣線程threadone將會優先於線程threadtwo執行並將占有更多的CPU時間該例中優先級設置放在線程啟動前也可以在啟動後進行設置以滿足不同的優先級需求
線程的(同步)控制
一個Java程序的多線程之間可以共享數據當線程以異步方式訪問共享數據時有時候是不安全的或者不和邏輯的比如同一時刻一個線程在讀取數據另外一個線程在處理數據當處理數據的線程沒有等到讀取數據的線程讀取完畢就去處理數據必然得到錯誤的處理結果這和我們前面提到的讀取數據和處理數據並行多任務並不矛盾這兒指的是處理數據的線程不能處理當前還沒有讀取結束的數據但是可以處理其它的數據
如果我們采用多線程同步控制機制等到第一個線程讀取完數據第二個線程才能處理該數據就會避免錯誤可見線程同步是多線程編程的一個相當重要的技術
在講線程的同步控制前我們需要交代如下概念
用Java關鍵字synchonized同步對共享數據操作的方法
在一個對象中用synchonized聲明的方法為同步方法Java中有一個同步模型監視器負責管理線程對對象中的同步方法的訪問它的原理是賦予該對象唯一一把鑰匙當多個線程進入對象只有取得該對象鑰匙的線程才可以訪問同步方法其它線程在該對象中等待直到該線程用wait()方法放棄這把鑰匙其它等待的線程搶占該鑰匙搶占到鑰匙的線程後才可得以執行而沒有取得鑰匙的線程仍被阻塞在該對象中等待
file://聲明同步的一種方式將方法聲明同步
class store
{
public synchonized void store_in()
{
…
}
public synchonized void store_out()
{
…
}
}
利用wait()notify()及notifyAll()方法發送消息實現線程間的相互聯系
Java程序中多個線程通過消息來實現互動聯系的這幾種方法實現了線程間的消息發送例如定義一個對象的synchonized 方法同一時刻只能夠有一個線程訪問該對象中的同步方法其它線程被阻塞通常可以用notify()或notifyAll()方法喚醒其它一個或所有線程而使用wait()方法來使該線程處於阻塞狀態等待其它的線程用notify()喚醒
一個實際的例子就是生產和銷售生產單元將產品生產出來放在倉庫中銷售單元則從倉庫中提走產品在這個過程中銷售單元必須在倉庫中有產品時才能提貨如果倉庫中沒有產品則銷售單元必須等待
程序中假如我們定義一個倉庫類store該類的實例對象就相當於倉庫在store類中定義兩個成員方法store_in()用來模擬產品制造者往倉庫中添加產品strore_out()方法則用來模擬銷售者從倉庫中取走產品然後定義兩個線程類customer類其中的run()方法通過調用倉庫類中的store_out()從倉庫中取走產品模擬銷售者另外一個線程類producer中的run()方法通過調用倉庫類中的store_in()方法向倉庫添加產品模擬產品制造者在主類中創建並啟動線程實現向倉庫中添加產品或取走產品
如果倉庫類中的store_in() 和store_out()方法不聲明同步這就是個一般的多線程我們知道一個程序中的多線程是交替執行的運行也是無序的這樣就可能存在這樣的問題
倉庫中沒有產品了銷售者還在不斷光顧而且還不停的在取產品這在現實中是不可思義的在程序中就表現為負值如果將倉庫類中的stroe_in()和store_out()方法聲明同步如上例所示就控制了同一時刻只能有一個線程訪問倉庫對象中的同步方法即一個生產類線程訪問被聲明為同步的store_in()方法時其它線程將不能夠訪問對象中的store_out()同步方法當然也不能訪問store_in()方法必須等到該線程調用wait()方法放棄鑰匙其它線程才有機會訪問同步方法
這個原理實際中也很好理解當生產者(producer)取得倉庫唯一的鑰匙就向倉庫中添放產品此時其它的銷售者(customer可以是一個或多個)不可能取得鑰匙只有當生產者添放產品結束交還鑰匙並且通知銷售者不同的銷售者根據取得鑰匙的先後與否決定是否可以進入倉庫中提走產品
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27683.html