10.1 多線程的概念
10.1.1 程序、進程和多任務
程序:是對數據描述與操作的代碼的集合,是應用程序執(zhí)行的腳本
進程:是程序的一次執(zhí)行過程,程序是靜態(tài)的,進程是動態(tài)的。系統(tǒng)運行一個程序就是一個進程從創(chuàng)建、運行到消亡的過程。
多任務:一個系統(tǒng)中可以同時運行多個程序。一個CPU同時只能執(zhí)行一個程序的一條指令,多任務運行的并發(fā)機制使這些任務交替運行。
10.1.2 線程
線程也稱為輕型進程(LWP),是比進程更小的運行單位,一個進程可以被劃分成多個線程。當一個程序執(zhí)行多線程時,可以運行兩個或多個由同一個程序啟動的任務。這樣一個程序可以使得多個活動任務同時發(fā)生。
10.1.3 多線程
與進程不同的是,同類多線程共享一塊內存空間和一組系統(tǒng)資源,所以系統(tǒng)創(chuàng)建多線程開銷相對較小。
10.1.4 線程的生命同期與Java的多線程機制
1.線程的生命同期與狀態(tài)
線程有創(chuàng)建(New)、可運行(Runnable)、運行中(Running)、掛起(Not Runnable)、死亡(Dead)5種狀態(tài)。
2.Java的多線程機制
java.lang中的線程類Thread封裝了所有需要的線程操作控制,有很多方法用來控制一個線程的運行、休眠、掛起或停止。
10.2 創(chuàng)建線程
建立一個線程需要完成三件事:
> 建立一個虛擬的CPU
> 給出線程的執(zhí)行代碼
> 提供代碼所操作的數據
兩種方法可以創(chuàng)建線程:一種方法是通過繼承線程類Thread來創(chuàng)建線程類;另一種方法是建立一個實現(xiàn)Runnable接口的類一創(chuàng)建線程 。
10.2.1 通過繼承Thread類創(chuàng)建線程
繼承Thread類這種方法中,需要覆蓋run( )方法來提供線程的執(zhí)行代碼,定義Thread類的成員變量來提供線程的數據。線程執(zhí)行時,從它的run( )方法中開始執(zhí)行,run()方法是線程執(zhí)行的起點
例:public class SC extends Thread{
int count=1,number;
SC(int num){
number=num;
System.out.println("創(chuàng)建線程"+number); }
public void run(){
while(true){
System.out.println("線程"+number+":計數"+count);
if(++count==3) return; } }
public static void main(String args[]){
for (int i=0;i<3;i++)
new SC(i+1).start(); } }
運行結果:創(chuàng)建線程1
創(chuàng)建線程2
創(chuàng)建線程3
線程1:計數1
線程1:計數2
線程2:計數1
線程2:計數2
線程3:計數1
線程3:計數2
例:P208
public class SC extends Thread{
public static void main(String args[]){
testThread t1=new testThread("線程1");//僅是一空線程對象,沒有啟動這一線程,系統(tǒng)不為它分配資源,
testThread t2=new testThread("線程2");
t1.start(); t2.start(); } } //啟動線程
class testThread extends Thread{
public testThread(String str)
{super(str);} //調用父類構造方法為線程對象命名
public void run(){
for (int i=0;i<3;i++){
System.out.println(getName()+"在運行"); //getName返回線程名稱
try
{sleep(1000); //用休眠1000毫秒來區(qū)分哪個線程在運行
System.out.println(getName()+"在休眠");}
catch(InterruptedException e){} }
System.out.println(getName()+"已結束"); }}
說明:由繼承Thread創(chuàng)建的子類,必須覆蓋run方法,因為run方法是abstact抽象方法
10.2.2 通過Runnable接口創(chuàng)建線程
如果是Applet類應不能再繼承Thread類(不能多繼承),這時可以通過接口Runnable直接創(chuàng)建線程對象。接口中只聲明了一個未實現(xiàn)的run方法。
線程體的構造方法:
public Thread([ThreadGroup group][,Runnable target][,String name])
其中group指明該線程所屬的線程組,target是執(zhí)行線程體的目標對象,它必須實現(xiàn)接口Runnable,name則為線程名。這三個參數均可任意沒有。
任何實現(xiàn)接口Runnable的對象都可以作為一個線程的目標對象,類Thread本身也實現(xiàn)了接口Runnable,因此我們可以通過兩種方法實現(xiàn)線程體。
(1)定義一個線程類,它繼承類Thread并重寫其中的方法run(),這時在初始化這個類的實例時,目標對象target可為null
(2)提供一個實現(xiàn)接口Runnable類作為線程的目標對象,在初始化一個Thread類或者Thread子類的對象時,把目標對象傳遞給這個線程實例,由該目標對象提供線程體run()。這時實現(xiàn)接口Runnable的類仍然要以繼承其他父類。
例:P210
import java.awt.*;
import java.applet.Applet;
import java.util.*;
import java.text.DateFormat;
public class SC extends Applet implements Runnable{
Thread clockThread=null;
public void init(){
setBackground(Color.blue);
setForeground(Color.yellow); }
public void start(){
if(clockThread==null){
clockThread=new Thread(this,"Clock2");
//Thread(ThreadGroup group,String name)name是線程名,group指定線程所屬的組
clockThread.start(); } }
public void run(){
Thread myThread=Thread.currentThread();
//找到當前執(zhí)行的線程,返回它的引用
while(clockThread==myThread){
repaint();
try{ Thread.sleep(1000);
} catch(InterruptedException e){}
} }
public void paint(Graphics g){
Date date=new Date();
DateFormat formatter=DateFormat.getTimeInstance();
String str=formatter.format(date);
g.drawString(str,5,10); }
public void stop()
{clockThread=null;} }
10.2.3 可運行狀態(tài)(Runnable)
Thread MyThread=new Thread()
MyThread.start();
這樣該線程處于可運行(Runnable)狀態(tài),注意,這狀態(tài)并不是運行中狀態(tài)(Running),因為線程也許實際并未真正運行(因很多計算機是單CPU)。當一個線程正在運行時,它是可運行的,并也是當前正運行的線程
10.2.4 不可運行狀態(tài)(Not Runnable)
當下面四種情況發(fā)生時,線程就是進入不可運行狀態(tài):
(1)調用了sleep()方法
(2)調用了suspend()方法
(3)為等候一個條件變量,線程調用wait()方法
(4)輸入輸出流中發(fā)生線程阻塞
我們來看看下面這個例子:
Thread myThread=new Thread();
MyThread.start();
try{ myThread.sleep(10000);}
catch(InterrupedException e){
}
對于上面四種情況,都有特定的返回可運行狀態(tài)的方法與之對應,對應方法如下:
(1)如果線程處于睡眠狀態(tài)中,sleep()方法中的參數為休息時間,當時間過去后,線程即為可運行的。
(2)如果一線程被掛起,須由其他線程調用resume()方法來恢復該線程的執(zhí)行。
?。?)如果線程在等待條件變量,那么要停止等待的話,要該條件變量所在的對象調用notify(),notifyAll()方法。
(4)如果在I/O流中發(fā)生線程阻塞,則特定的I/O指令將結束這種不可運行狀態(tài)
10.2.5 死亡狀態(tài)(Dead)
一般可通過兩種方法實現(xiàn):自然撤消或是被停止
1.自然撤消
public void run(){
int i=0;
while(i<100){
i++;
System.out.println(“i=”+i); }}
當run()方法結束后,該線程應自然撤消了
2.被停止
Thread myThread=new Thread();
MyThread.start();
try{Trhead.currentThread().sleep(10000);
} catch(InterruptedException e)
myThread.stop();
10.2.6 非法狀態(tài)處理(IllegalThreadStateException)
當一個線程剛被創(chuàng)建后,只能對它調用start()或stop()方法,若調用其他方法則會引起非法狀態(tài)處理。同樣對于任何狀態(tài),如果所調用的方法與狀態(tài)不符,都會引起非法狀態(tài)處理。
10.3 線程的優(yōu)先級
Java為使有些線程可以提前得到服務,可給線程設置優(yōu)先級。在單個CPU上運行多線程時采用了線程隊列技術,Java虛擬機支持固定優(yōu)先級隊列,一個線程的執(zhí)行順序取決于其對其他Runnable線程的優(yōu)先級。優(yōu)先級分1~10同,默認級別是5級。
Thread的公用靜態(tài)常量表示:
public static final int MAX_PRORITY=10 (最大優(yōu)先級10)
public static final int MIN_PRORITY=1 (最小優(yōu)先級10)
public static final int NORM_PRORITY=5 (默認優(yōu)先級5)
public final int getPriority( ) //獲得線程優(yōu)先級
public final setPriority(int newPriority) //設置線程優(yōu)先級
10.4 線程的調度與控制
10.4.1 線程類的方法
1.線程的類方法
Thread類的靜態(tài)方法,即可直接從Thread類調用
CurrentThread( ):返回正在運行的Thread對象名稱
sleep(int n):讓當前線程休眠n毫秒
2.實例方法P213
10.4.2 控制線程的狀態(tài)
1.掛起一個線程:suspend( )方法
t.suspend():將t暫停執(zhí)行,必須由其他線程調用t.resume()恢復,不提倡使用該方法,因為容易造成死鎖
2.停止一個線程:stop( )方法
當線程完成運行并結束后,將不能再運行。另不可用t.stop()強行終止線程。注:這并沒有消滅這個線程,只是停止線程的執(zhí)行。但這個線程不能用t.start()重新啟動。不提倡采用這種方法,易造成線程的不一致。要通過設置flag通知一個線程應該結束。
3.線程休眠:sleep(long )方法
Thread.sleep()使一個線程暫停運行一段固定時間。
4.連接線程:join( )方法
t.join()使當前的線程等待,直到t結束為止,線程恢復到可運行狀態(tài)。三種調用格式:
(1)join():如當前線程發(fā)出調用t.join(),則當前線程將等待線程t結束后再繼續(xù)執(zhí)行。
?。?)join(long millis):如當前線程發(fā)出調用t.join(long millis),則當前線程將等待線程t結束或最多等待mills毫秒后再繼續(xù)執(zhí)行。
?。?)join(long millis,int nanos):如當前線程發(fā)出調用t.join(long millis,int nanos),則當前線程將等待線程t結束或最多等待mills毫秒+nanos納秒后再繼續(xù)執(zhí)行。
5.暫停(退讓)線程:yield( )方法(只讓給同優(yōu)先級運行)
調用t.yield()方法可暫停當前運行線程,但處于可運行狀態(tài),讓同優(yōu)先級線程先運行。若沒有同等優(yōu)先級的線程是可運行狀態(tài),yield方法將什么也不做。
6.中斷線程:interrupt( )方法
如果一個線程t在調用sleep(),join(),wait()方法被阻塞時,則t.interrupt()方法將使t的中斷狀態(tài)被清除,中斷t的阻塞狀態(tài),并且將接收到InterruptException異常。
一個線程可以通過獲取另一個線程的引用,調用該線程的interrupt()方法來中斷另一個sleep或wait線程的執(zhí)行
7.了解線程的狀態(tài):isAlive( )方法
返回true則線程處于Runnable或Not Runnable狀態(tài)(即已啟動但還沒有運行結束),返回false則說明線程處于New Thread或Dead狀態(tài)
start() 調度
時間片到y(tǒng)ield() run(),stop()結束
時間到 sleep()
interrupt() join() wait()
synchronized()
獲得互斥
使用權
notify()
notifyall()
線程狀態(tài)的轉換圖
例:public class SC{
public static void main(String args[]) throws Exception{
int i=0;
Hello t=new Hello();
t.start();
while(true){
System.out.println("早上好!"+i++);
if (i==2&&t.isAlive()){
System.out.println("Main waiting for Hello!");
t.join(); //等待t運行結束
}
if (i==4) break; }}}
class Hello extends Thread{
int i;
public void run(){
while (true){
System.out.println("嗨"+i++);
if(i==4) break; } } }
運行結果:
早上好!0
早上好!1
Main waiting for Hello!
嗨0
嗨1
嗨2
嗨3
早上好!2
早上好!3
10.5 線程的同步機制與共享資源(自學)