天天看點

多線程 建立線程的兩種方式

建立線程有兩種方式:第一種是繼承Thread類,第二種是實作Runnable接口。下面詳細介紹兩種線程建立的方式,這個内容為必須掌握的。到最後會對比兩種方式建立線程的優勢和劣勢。

1繼承Thread類建立線程:

1,建立一個類,繼承Thread類并覆寫Thread類的run方法。為什麼要覆寫run方法?該run方法的方法體就是代表了線程要完成的任務,是以,run方法也稱為線程執行體。

2,建立Thread子類的執行個體,即建立了線程對象。

3,用線程對象調用start方法啟動線程。

下面來看看代碼是如何實作的:

public class ThreadCreat
{
	public static void main(String args[]){
		MyThread mt = new MyThread(); //建立Thread子類執行個體對象
		mt.start(); //通過子類對象的start方法開啟線程
		for(int x=0;x<10;x++)
			System.out.println(Thread.currentThread().getName()+"-->"+x);
		
	} 
}
class MyThread extends Thread  //建立一個類繼承Thread
{
	public void run(){  //覆寫run方法,方體即線程體
		for(int i=0;i<10;i++)
			System.out.println(this.getName()+"--->"+i);
	}
}
           

---------- 運作java程式 ----------

main-->0

main-->1

Thread-0--->0

Thread-0--->1

main-->2

main-->3

main-->4

main-->5

main-->6

main-->7

main-->8

main-->9

Thread-0--->2

Thread-0--->3

Thread-0--->4

Thread-0--->5

Thread-0--->6

Thread-0--->7

Thread-0--->8

Thread-0--->9

輸出完成 (耗時 0 秒) - 正常終止

在列印時,我使用了Thread類中靜态方法currentThread ,調用該方法可以傳回目前正在執行的線程對象(Thread)。從輸出可以看到,在本程式中,開啟了兩條線程,一條是主線程:

main,另外一條是Thread-0線程。這兩條線程是随機交替進行的。對于單核cpu在某一個時間點上隻能執行一個命名,由于cpu切換的非常快,人是無法察覺它的切換,給人的感覺實在同時執行多個任務。知道了這一點,有利于我們了解多線程。

下面再來看看為何會出現上面的輸出結果:

多線程 建立線程的兩種方式

2線程建立的第二種方式:實作Runnable接口

1,建立一個類實作Runnable接口,并覆寫run方法,run方法體就是線程體。

2,建立一個Thread本類對象,在建立該對象時,使用的是其Thread(Runnable  target)構造方法,将Runnable 子類對象作為參數傳遞給該構造方法。

3,調用Thread的start方法,啟動線程。

下面看看代碼是如何實作的:

public class ThreadCreat2
{
	public static void main(String args[]){
		new Thread(new MyThread2()).start();//将實作了Runnable接口的類的執行個體對象作為                                         //參數傳遞給Thread類的執行個體對象
		for(int i=0;i<10;i++)
			System.out.println(Thread.currentThread().getName()+"-->"+i);
	}
}
class MyThread2 implements Runnable //實作Runnable接口
{
	public void run(){
		for(int i=0;i<10;i++)
			System.out.println(Thread.currentThread().getName()+"-->"+i);
	}
}
           

---------- 運作java程式 ----------

main-->0

main-->1

main-->2

main-->3

main-->4

main-->5

main-->6

Thread-0-->0

Thread-0-->1

main-->7

main-->8

main-->9

Thread-0-->2

Thread-0-->3

Thread-0-->4

Thread-0-->5

Thread-0-->6

Thread-0-->7

Thread-0-->8

Thread-0-->9

輸出完成 (耗時 0 秒) - 正常終止

從運作結果也可以看到,主線程(main)和Thread-0線程時随機交替執行的。

看完兩種建立線程的方式後,我們來對比下兩種線程的優勢和劣勢:

采用繼承Thread類方式完成多線程建立:

劣勢是:因為線程類已經繼承了Thread類,是以不能再繼承其他父類。

優勢是:編寫簡單,如果需要通路線程,無需使用Thread.currentThread()方法,直接

使用this即可獲得目前線程。

采用實作Runnable接口方式的多線程:

優勢是:線程類隻是實作了Runnable接口,還可以繼承其他類。在這種方式下,可以多個線程共享同一個target對象,是以非常适合多個相同線程來處理同一份資源的情況,較好的展現了面向對象的思想。

劣勢是:程式設計稍稍複雜,如果需要通路目前線程,必須使用Thread.currentThread()方法。

推薦使用實作Runnable接口的方式建立線程對象。

3,售票程式

現在用多線程來模拟一個實際情況:

現在有四個售票廳,同時在賣100張票,這100張票是他們共有的,售完即停。

我們先來看下代碼是如何實作的:

public class SoldTickets
{
	public static void main(String args[]){
		Tickets t1 = new Tickets();
		Tickets t2 = new Tickets();
		Tickets t3 = new Tickets();
		Tickets t4 = new Tickets();//建立四個線程對象

		t1.start(); //開啟四個賣票線程
		t2.start();
	    t3.start();
		t4.start();
	}
}
class Tickets extends Thread
{
	public static int ticket = 100;  //這裡加靜态時為了保證賣的票是100張
    static Object obj = new Object();  //要保證鎖的唯一性,就需要用static關鍵字修飾
	public void run(){
		while(true){
			synchronized(obj){  //線程使用的是同一個鎖
					if(ticket>0){
					System.out.println(this.getName()+"-->"+ticket);
					ticket--;
				}
			}
		}
	}
}
           

上面是通過繼承Thread類來完成賣票程式的。重點是要保證多個線程處理的是同一份資料,其二,關鍵代碼要用同步代碼塊。在以上代碼中是如何實作的了?

我們來看高亮的注釋:

由于是繼承Thread類,要開啟多條線程,就會建立多個對象,為了保證賣的票是唯一的

public static int ticket = 100; 不管我們建立多少對象,用static關鍵字修飾的成員變量,在記憶體中隻有一份。至于另一個問題,同步代碼塊和鎖的問題會留在下一節會詳細講解,并給出為何要同步的原因。

再來看看實作Runnable接口的類是如何完成賣票程式的: 

public class SoldTickets2
{
	public static void main(String args[]){
		TicketsDemo td = new TicketsDemo();
		Thread t1 = new Thread(td);
		Thread t2 = new Thread(td);
		Thread t3 = new Thread(td);
		Thread t4 = new Thread(td);

		t1.start();
	    t2.start();
		t3.start();
		t4.start();
	}
}
class TicketsDemo implements Runnable
{
	public int tickets = 100;   //注意這裡并沒有加static
	Object obj = new Object(); //這裡也沒有加static
	public void run(){
		while(true){
			synchronized(obj){  //加鎖
				if(tickets>0){
					System.out.println(Thread.currentThread().getName()+"-->"+tickets);
					tickets--;
				}
			}
		}
	}
}
           

我們來看看,這段代碼是如何保證賣的是共同的100張票。

由于我們是采用的是實作Runnable接口方式來建立線程的。我們隻需要建立一個TicketsDemo類執行個體對象;在建立線程對象時通過建立Thread本類對象來完成的,同時将TicketsDemo作為參數傳遞給Thread的有參構造器,完成線程的建立。這樣一來,我們可以用同一個TicketsDemo對象來建立多個線程對象。這就保證了票的唯一性,鎖的唯一性也保證了。