? ? ? 本系列譯自jakob jenkov的Java并發(fā)多線程教程,個(gè)人覺(jué)得很有收獲。由于個(gè)人水平有限,不對(duì)之處還望矯正!
創(chuàng)建和啟動(dòng)線程
在java中創(chuàng)建一個(gè)線程如下:
Thread thread = new Thread();
調(diào)用方法start()來(lái)啟動(dòng)一個(gè)線程:
thread.start();
? ? ? ? 這個(gè)例子沒(méi)有指定線程執(zhí)行任何代碼,線程將會(huì)在啟動(dòng)之后停止。
? ? ? ? 有兩種方式指定線程應(yīng)該執(zhí)行什么代碼。第一種方式就是創(chuàng)建一個(gè)Thread的子類并覆寫(xiě)run()方法。第二種方式就是創(chuàng)建一個(gè)實(shí)現(xiàn)Runnable接口的類。
Thread的子類
? ? ? 第一種方式指定線程執(zhí)行什么樣的代碼,就是創(chuàng)建一個(gè)Thread的子類,并且覆寫(xiě)run()方法。在run()方法里的代碼就是你調(diào)用start()方法后,線程要執(zhí)行的代碼。下面是一個(gè)創(chuàng)建Thread子類的例子:
public class MyThread extends Thread{
? ? ? ?@Override
? ? ? ? public void run(){
? ? ? ? ? ? ?System.out.println("MyThread running");
? ? ? ? }
}
為了創(chuàng)建并啟動(dòng)上面的線程,你應(yīng)該這樣做:
MyThread myThread = new MyThread();
myThread.start();
start()方法會(huì)在線程開(kāi)始后立馬返回,而不是等到run()方法執(zhí)行完畢。當(dāng)run()執(zhí)行時(shí),就會(huì)輸出“MyThread running”;
當(dāng)然,你也可以創(chuàng)建一個(gè)Thread的匿名子類,如下:
Thread thread = new Thread(){
? ? ? @Override
? ? ? ?public void run(){
? ? ? ? ? ? ? ? System.out.println("Thread Running");
? ? ? ?}
}
thread.start();
上面的例子當(dāng)線程被調(diào)用時(shí)會(huì)輸出文本“Thread Running".
實(shí)現(xiàn)Runnable接口
? ? ?第二種方式指定線程應(yīng)該執(zhí)行什么樣的代碼,就是創(chuàng)建一個(gè)實(shí)現(xiàn)java.lang.Runnable接口的類。這個(gè)Runnable對(duì)象可以被Thread執(zhí)行。
? ? ?下面是一個(gè)實(shí)現(xiàn)了Runnable接口的例子:
public class MyRunnable implements Runnable{
? ? ? @Override
? ? ? public void run(){
? ? ? ? ? ? System.out.println("MyRunnable running");
? ? ? }
}
因?yàn)橛辛薚hread線程執(zhí)行的run()方法,將MyRunnable的一個(gè)實(shí)例傳給Thread的構(gòu)造方法。
Thread thread = new Thread(new MyRunnable());
thread.start();
? ? 當(dāng)線程啟動(dòng)時(shí),會(huì)調(diào)用MyRunnable實(shí)例中的run()方法,而不是Thread自己的run()方法。上面的例子會(huì)輸出”MyRunnable running".
當(dāng)然,你也可以創(chuàng)建一個(gè)匿名的Runnable接口實(shí)例:
Runnable myRunnable = new Runnable(){
? ? ? ? @Override
? ? ? ? public void run(){
? ? ? ? ? ? ? ?System.out.println("Runnable running");
? ? ? ? ?}
}
Thread thread = new Thread(myRunnable);
thread.start();
Subclass or Runnable?
? ? ? ?沒(méi)有明確的規(guī)則說(shuō)這兩種方式哪一種是最好的。個(gè)人傾向于實(shí)現(xiàn)Runnable接口。將實(shí)現(xiàn)Runable接口的一個(gè)實(shí)例交給Thread的實(shí)例。當(dāng)由線程池來(lái)執(zhí)行實(shí)現(xiàn)Runnable接口的線程實(shí)例時(shí),當(dāng)線程池沒(méi)有空閑線程可以調(diào)試時(shí),可以讓這些線程很好的排隊(duì)。但是如果執(zhí)行的是實(shí)現(xiàn)Thread的子類的線程實(shí)例,那么將會(huì)很難做到這一點(diǎn)。
? ? ? ?有時(shí),你可能要同時(shí)實(shí)現(xiàn)Runnable和Thread子類。例如:創(chuàng)建一個(gè)Thread的線程可以執(zhí)行一個(gè)或多個(gè)Runable實(shí)例,這就是線程池的實(shí)現(xiàn)方式。
常見(jiàn)的陷阱:調(diào)用run()方法而不是start()方法
? ? ? 當(dāng)創(chuàng)建和啟動(dòng)一個(gè)線程,通常會(huì)犯的一個(gè)錯(cuò)誤就是調(diào)用run()方法,而不是start()方法,如下:
Thread newThread = new Thread(MyRunnable());
newThread.run(); // should be start();
起初,你可能沒(méi)有注意到什么,因?yàn)閞un()正如你期待的那樣被執(zhí)行了。然而,他并不是被你剛創(chuàng)建的線程所執(zhí)行。而是被創(chuàng)建線程的線程執(zhí)行。換句話說(shuō),就是執(zhí)行上面兩行代碼的線程來(lái)執(zhí)行的run()里的方法。調(diào)用線程的使用start()方法。
線程名稱
? ? ? ?當(dāng)你創(chuàng)建一個(gè)線程時(shí),你可以給這個(gè)線程指定名稱。線程名可以讓你和其他的線程進(jìn)行區(qū)分。舉個(gè)例子:
Thread thread = new Thread("New Thread"){
? ? ? ?@override
? ? ? ?public void run(){
? ? ? ? ? ? ? System.out.println("run by:"+getName());
? ? ? }
}
thread.start();
System.out.println(thread.getName());
注意,字符串“New Thread"作為一個(gè)參數(shù)傳給Thread的構(gòu)造器,這個(gè)字符串就是線程的名稱,這個(gè)名稱可以通過(guò)方法getName()來(lái)獲取到,你也可以傳遞參數(shù)的方式給一個(gè)實(shí)現(xiàn)Runnable的接口的線程指定線程名稱:如下
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable,"New Thread");
thread.start();
System.out.println(thread.getName());
注意。MyRunnable不是Thread的一個(gè)子類,他不能直接調(diào)用Thread的getName()方法。
Thread.currentThread()
Thread.currentThread()方法返回線程正在執(zhí)行的線程。
Thread thread = Thread.currentThread();
只要獲取到當(dāng)前運(yùn)行線程,你就可以在此基礎(chǔ)上進(jìn)行方法的調(diào)用。例如:你可以獲取到當(dāng)前正在執(zhí)行線程的名稱。
String threadName = Thread.currentThread().getName();
Java Thread example
這里有一個(gè)小例子。首先輸出執(zhí)行main方法的線程名稱。這個(gè)線程是由JVM指定的。然后開(kāi)啟10個(gè)線程,并以”“+i作為他們的線程名。每個(gè)線程輸出他們的名字后,然后停止。
public class ThreadExample{
? ? ?public static void main(String[] args){
? ? ? ? ? ?System.out.println(Thread.currentThread().getName());
? ? ? ? ? ?for(int i=0;i<10;i++){
? ? ? ? ? ? ? new Thread(""+i){
? ? ? ? ? ? ? ? ? ?public void run(){
? ? ? ? ? ? ? ? ? ? ? ?System.out.println("Thread:"+getName()+"running");
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ?}.start();
? ? ? ? ?}
? ? ? }
}
注意。線程并不是有序執(zhí)行的。也就是說(shuō)線程1并不是第一個(gè)執(zhí)行的線程,這是因?yàn)榫€程的執(zhí)行原則是并行的,而不是有序的,JVM和操作系統(tǒng)決線程的調(diào)度順序。當(dāng)他們調(diào)度時(shí)順序是不固定的。