第五章 多線程
第五講
多線程
● 在多任務(wù)系統(tǒng)中,每個獨(dú)立執(zhí)行的程序稱為進(jìn)程,也就是“正在進(jìn)行的程序”。我們現(xiàn)在使用的操作系統(tǒng)一般都是多任務(wù)的,即能夠同時執(zhí)行多個應(yīng)用程序,實(shí)際情況是,操作系統(tǒng)負(fù)責(zé)CPU等設(shè)備的資源進(jìn)行分配和管理,雖然這些設(shè)備某一時刻只能做一件事,但以非常小的時間間隔交替執(zhí)行多個程序,就可以給人以同時執(zhí)行多個程序的感覺。
● 一個進(jìn)程中又可以包含一個或多個線程,一個線程就是一個程序內(nèi)部的一條執(zhí)行線索,如果要一程序中實(shí)現(xiàn)多段代碼同時交替運(yùn)行,就需產(chǎn)生多個線程,并指定每個線程上所要運(yùn)行的代碼段,這就是多線程。
用Thread類創(chuàng)建線程
● 要將一段代碼在一個新的線程上運(yùn)行,該代碼應(yīng)該在一個類的run函數(shù)中,并且run函數(shù)所在的類是Thread類的子類。倒過來看,我們要實(shí)現(xiàn)多線程,必須編寫一個繼承了Thread類的子類,子類要覆蓋Thread類中的run函數(shù),在子類的run函數(shù)中調(diào)用想在新線程上運(yùn)行的程序代碼。
● 啟動一個新的線程,我們不是直接調(diào)用Thread的子類對象的run方法,而是調(diào)用Thread子類對象的start(從Thread類中繼承到的)方法。Thread類對象的start方法將產(chǎn)生一個新的線程,并在該線程上運(yùn)行該Thread類對象的run方法。根據(jù)面向?qū)ο蟮倪\(yùn)行時的多態(tài)性,在該線程上實(shí)際運(yùn)行的是Thread子類(也就是我們寫的那個類)對象中的run方法。
● 由于線程的代碼段在run方法中,那么該方法執(zhí)行完成以后線程也就相應(yīng)的結(jié)束了,因而我們可以通過控制run方法中的循環(huán)的條件來控制線程的結(jié)束。
class SubThread extends Thread
{
public void run()
{
while(true)
{
System.out.println("SubThread: "+Thread.currentThread().getName());
}
}
}
class TestThread
{
public static void main(String[]args)
{
//SubThread st=new SubThread();
//st.start();
new SubThread().start();
while(true)
{
System.out.println("main: "+Thread.currentThread().getName());
}
}
}
后臺線程與聯(lián)合線程
● 如果我們對某個線程對象在啟動(調(diào)用start方法)之前調(diào)用了setDaemon(true)方法,這個線程就變成了后臺線程。
● 對java程序來說,只要還有一個前臺線程在運(yùn)行,這個進(jìn)程就不會結(jié)束,如果一個進(jìn)程中只有后臺線程運(yùn)行,這個進(jìn)程就會結(jié)束。
class ThreadDemo1
{
public static void main(String[]args)
{
Thread tt=new TestThread();
tt.setDaemon(true);
tt.start();//run();
}
}
class TestThread extends Thread
{
public void run()
{
while(true)
{
System.out.println("run():"+Thread.currentThread().getName());
}
}
}
● pp.join()的作用是把pp所對應(yīng)的線程合并到調(diào)用pp.join();語句的線程中。
例如:
class ThreadDemo1
{
public static void main(String[]args)
{
Thread tt=new TestThread();
//tt.setDaemon(true);
tt.start();
int index=0;
//在index=100后,tt線程join主線程中,主線程停止。在tt線程運(yùn)行10秒后,主線程和tt線程又交替運(yùn)行。join()中也可不帶參數(shù)。
while(true)
{
if(++index==100)
try{tt.join(10000);}catch(Exception e){}
System.out.println("mian():"+Thread.currentThread().getName());
}
}
}
class TestThread extends Thread
{
public void run()
{
while(true)
{
System.out.println("run():"+Thread.currentThread().getName());
}
}
}
實(shí)現(xiàn)Runnable接口的多線程方法
class ThreadDemo1
{
public static void main(String[]args)
{
//Thread tt=new TestThread();
//tt.setDaemon(true);
Thread tt=new Thread(new TestThread());
tt.start();
int index=0;
//在index=100后,tt線程join主線程中,在tt線程運(yùn)行10秒后,主線程和tt線程交替運(yùn)行。join()中也可不帶參數(shù)。
while(true)
{
if(++index==100)
try{tt.join(10000);}catch(Exception e){}
System.out.println("mian():"+Thread.currentThread().getName());
}
}
}
class TestThread implements Runnable//extends Thread
{
public void run()
{
while(true)
{
System.out.println("run():"+Thread.currentThread().getName());
}
}
}
使用Runnable接口創(chuàng)建多線程
● 適合多個相同程序代碼的線程去處理同一資源的情況,把虛擬CPU(線程)同程序的代碼、數(shù)據(jù)有效分離,較好地體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想。
● 可以避免由于Java的單繼承性帶來的局限。我們經(jīng)常碰到這樣一種情況,即當(dāng)我們要將已經(jīng)繼承了其一個類的子類放入多線程中,由于一個類不能同時有兩個父類,所以不能繼承Thread類的方式,那么,這個類只能采用實(shí)現(xiàn)Runnable。
● 當(dāng)線程被構(gòu)造時,需要的代碼和數(shù)據(jù)通過一個對象作為構(gòu)造函數(shù)的實(shí)參傳遞進(jìn)去,這個對象實(shí)現(xiàn)了Runnable接口的類的實(shí)例。
● 事實(shí)上,幾乎所有多線程應(yīng)用都可用Runnable接口方式。
class ThreadDemo1
{
public static void main(String[]args)
{
TestThread t=new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable
{
//int tickets=100;
int tickets=1;
public void run()
{
/*while(true)
{
if(tickets>0)
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets--);
} */
for(;tickets<101;tickets++)
{
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets);
}
}
}
多線程的同步
● 使用同步代碼塊來實(shí)現(xiàn)線程的同步
class ThreadDemo1
{
public static void main(String[]args)
{
TestThread t=new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable
{
String str=new String("");
int tickets=100;
//int tickets=1;
public void run()
{
while(true)
{
synchronized(str)
{
if(tickets>0)
{
try
{
Thread.sleep(10);
}catch(Exception e)
{
e.printStackTrace();
}
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets--);
}
}
}
/*synchronized(str)
{
for(;tickets<101;tickets++)
{
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}//讓線程暫停10毫秒
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets);
}
}*/
}
}
● 使用同步函數(shù)來實(shí)現(xiàn)線程的同步
class ThreadDemo1
{
public static void main(String[]args)
{
TestThread t=new TestThread();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
class TestThread implements Runnable
{
int tickets=100;
public void run()
{
while(true)
{
sale();
}
}
public synchronized void sale()
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}//讓線程暫停10毫秒
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets--);
}
}
}
● 代碼塊與函數(shù)間的同步
class ThreadDemo1
{
public static void main(String[]args)
{
TestThread t=new TestThread();
new Thread(t).start();
try{Thread.sleep(1);}catch(Exception e){}
t.str="method";
new Thread(t).start();
}
}
class TestThread implements Runnable
{
String str=new String("");
int tickets=100;
public void run()
{
if(str.equals("method"))
{
while(true)
{
sale();
}
}
else
{
while(true)
{
synchronized(this)
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}//讓線程暫停10毫秒
System.out.println("run():"+Thread.currentThread().getName()+" "+tickets--);
}
}
}
}
}
public synchronized void sale()
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}//讓線程暫停10毫秒
System.out.println("method():"+Thread.currentThread().getName()+" "+tickets--);
}
}
}
線程間的通信
● wait:告訴當(dāng)前線程放棄監(jiān)視器并進(jìn)入睡眠狀態(tài)直到其他線程進(jìn)入同一監(jiān)視器并調(diào)用notify為止。
● notify:喚醒同一對象監(jiān)視器中調(diào)用wait的第一線程。用于類似飯館有一個空位后通知所有等候就餐的顧客中的第一位可以入座的情況。
● notifyAll:喚醒同一對象監(jiān)視器中調(diào)用wait的所有線程,具有最高優(yōu)先級的線程首先被喚醒并執(zhí)行。
例:一線程向緩沖區(qū)中不斷放入”zhangsan”,”male”和”lisi”,”female”,另一線程不斷從緩沖中取走數(shù)據(jù)。
線程通信例程1:(在下面例程2的基礎(chǔ)上修改而來)
class Producer implements Runnable
{
Q q;
public Producer(Q q)
{
this.q=q;
}
public void run()
{
int i=0;
while(true)
{
if(i==0)
{
q.put("zhangsan","male");
}
else
{
q.put("lisi","female");
}
i=(i+1)%2;
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q=q;
}
public void run()
{
while(true)
{
q.get();
}
}
}
class Q
{
private String name="unknown";
private String sex="unknown";
private boolean bFull=false;//設(shè)置緩沖區(qū)狀態(tài),空
public synchronized void put(String name,String sex)
{
if(bFull)//如果緩沖區(qū)為滿(true),線程進(jìn)入睡眠狀態(tài)
try{wait();}catch(Exception e){}
this.name=name;
try{Thread.sleep(1);}catch(Exception e){}
this.sex=sex;
bFull=true;
notify();
}
public synchronized void get()
{
if(!bFull)//如果緩沖區(qū)為空,線程進(jìn)入睡眠狀態(tài)
try{wait();}catch(Exception e){}
System.out.print(name);
System.out.println(":"+sex);
bFull=false;
notify();
}
}
class ThreadCommunation
{
public static void main(String[]args)
{
Q q=new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
//sleep((int)(Math.random() * 100));
線程通信例程2:
class Producer implements Runnable
{
Q q;
public Producer(Q q)
{
this.q=q;
}
public void run()
{
int i=0;
while(true)
{
synchronized(q)
{
if(q.bFull)
try{q.wait();}catch(Exception e){}
//else
//{
if(i==0)
{
q.name="zhangsan";
try{Thread.sleep(1);}catch(Exception e){}
q.sex="male";
}
else
{
q.name="lisi";
q.sex="female";
}
q.bFull=true;
q.notify();
//}
}
i=(i+1)%2;
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q=q;
}
public void run()
{
while(true)
{
synchronized(q)
{
if(!q.bFull)
try{q.wait();}catch(Exception e){}
//else不能用else語句。
//{
System.out.print(q.name);
System.out.println(":"+q.sex);
q.bFull=false;
q.notify();
//}
}
}
}
}
class Q
{
String name="unknown";
String sex="unknown";
boolean bFull=false;
}
class ThreadCommunation
{
public static void main(String[]args)
{
Q q=new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
//sleep((int)(Math.random() * 100));
線程的生命的控制
● 程序中如何控制線程的生命
class ThreadTest implements Runnable
{
private boolean bStop=false;
public void stop()
{
bStop=true;
}
public void run()
{
while(!bStop)
{
System.out.println(Thread.currentThread().getName()+"is running.");
}
}
}
class TestThread
{
public static void main(String[]args)
{
ThreadTest tt=new ThreadTest();
new Thread(tt).start();
for(int i=0;i<100;i++)
{
if(i==50)
{
tt.stop();
}
System.out.println(Thread.currentThread().getName());
}
}
}
posted on 2007-06-08 10:46 大頭劍客 閱讀(330) 評論(0) 編輯 收藏 所屬分類: 學(xué)習(xí)筆記