鍍金池/ 教程/ Java/ 如何創(chuàng)建并運(yùn)行 java 線程
Slipped Conditions
阻塞隊(duì)列
無(wú)阻塞算法
嵌套管程鎖死
Java 并發(fā)性和多線程介紹
死鎖
線程安全及不可變性
并發(fā)編程模型
Java 中的讀/寫鎖
剖析同步器
競(jìng)態(tài)條件與臨界區(qū)
多線程的優(yōu)點(diǎn)
CAS
線程通信
如何創(chuàng)建并運(yùn)行 java 線程
阿姆達(dá)爾定律
避免死鎖
信號(hào)量
多線程的代價(jià)
饑餓和公平
線程池
重入鎖死
Java 中的鎖
Java 內(nèi)存模型
線程安全與共享資源
Java 同步塊

如何創(chuàng)建并運(yùn)行 java 線程

Java 線程類也是一個(gè) object 類,它的實(shí)例都繼承自 java.lang.Thread 或其子類。 可以用如下方式用 java 中創(chuàng)建一個(gè)線程:

Tread thread = new Thread();

執(zhí)行該線程可以調(diào)用該線程的 start()方法:

thread.start();

在上面的例子中,我們并沒有為線程編寫運(yùn)行代碼,因此調(diào)用該方法后線程就終止了。

編寫線程運(yùn)行時(shí)執(zhí)行的代碼有兩種方式:一種是創(chuàng)建 Thread 子類的一個(gè)實(shí)例并重寫 run 方法,第二種是創(chuàng)建類的時(shí)候?qū)崿F(xiàn) Runnable 接口。接下來(lái)我們會(huì)具體講解這兩種方法:

創(chuàng)建 Thread 的子類

創(chuàng)建 Thread 子類的一個(gè)實(shí)例并重寫 run 方法,run 方法會(huì)在調(diào)用 start()方法之后被執(zhí)行。例子如下:

public class MyThread extends Thread {
   public void run(){
     System.out.println("MyThread running");
   }
}

可以用如下方式創(chuàng)建并運(yùn)行上述 Thread 子類

MyThread myThread = new MyThread();
myTread.start();

一旦線程啟動(dòng)后 start 方法就會(huì)立即返回,而不會(huì)等待到 run 方法執(zhí)行完畢才返回。就好像 run 方法是在另外一個(gè) cpu 上執(zhí)行一樣。當(dāng) run 方法執(zhí)行后,將會(huì)打印出字符串 MyThread running。

你也可以如下創(chuàng)建一個(gè) Thread 的匿名子類:

Thread thread = new Thread(){
   public void run(){
     System.out.println("Thread Running");
   }
};
thread.start();

當(dāng)新的線程的 run 方法執(zhí)行以后,計(jì)算機(jī)將會(huì)打印出字符串”Thread Running”。

實(shí)現(xiàn) Runnable 接口

第二種編寫線程執(zhí)行代碼的方式是新建一個(gè)實(shí)現(xiàn)了 java.lang.Runnable 接口的類的實(shí)例,實(shí)例中的方法可以被線程調(diào)用。下面給出例子:

public class MyRunnable implements Runnable {
   public void run(){
    System.out.println("MyRunnable running");
   }
}

為了使線程能夠執(zhí)行 run()方法,需要在 Thread 類的構(gòu)造函數(shù)中傳入 MyRunnable 的實(shí)例對(duì)象。示例如下:

Thread thread = new Thread(new MyRunnable());
thread.start();

當(dāng)線程運(yùn)行時(shí),它將會(huì)調(diào)用實(shí)現(xiàn)了 Runnable 接口的 run 方法。上例中將會(huì)打印出”MyRunnable running”。

同樣,也可以創(chuàng)建一個(gè)實(shí)現(xiàn)了 Runnable 接口的匿名類,如下所示:

Runnable myRunnable = new Runnable(){
   public void run(){
     System.out.println("Runnable running");
   }
}
Thread thread = new Thread(myRunnable);
thread.start();

創(chuàng)建子類還是實(shí)現(xiàn) Runnable 接口?

對(duì)于這兩種方式哪種好并沒有一個(gè)確定的答案,它們都能滿足要求。就我個(gè)人意見,我更傾向于實(shí)現(xiàn) Runnable 接口這種方法。因?yàn)榫€程池可以有效的管理實(shí)現(xiàn)了 Runnable 接口的線程,如果線程池滿了,新的線程就會(huì)排隊(duì)等候執(zhí)行,直到線程池空閑出來(lái)為止。而如果線程是通過(guò)實(shí)現(xiàn) Thread 子類實(shí)現(xiàn)的,這將會(huì)復(fù)雜一些。

有時(shí)我們要同時(shí)融合實(shí)現(xiàn) Runnable 接口和 Thread 子類兩種方式。例如,實(shí)現(xiàn)了 Thread 子類的實(shí)例可以執(zhí)行多個(gè)實(shí)現(xiàn)了 Runnable 接口的線程。一個(gè)典型的應(yīng)用就是線程池。

常見錯(cuò)誤:調(diào)用 run()方法而非 start()方法

創(chuàng)建并運(yùn)行一個(gè)線程所犯的常見錯(cuò)誤是調(diào)用線程的 run()方法而非 start()方法,如下所示:

Thread newThread = new Thread(MyRunnable());
newThread.run();  //should be start();

起初你并不會(huì)感覺到有什么不妥,因?yàn)?run()方法的確如你所愿的被調(diào)用了。但是,事實(shí)上,run()方法并非是由剛創(chuàng)建的新線程所執(zhí)行的,而是被創(chuàng)建新線程的當(dāng)前線程所執(zhí)行了。也就是被執(zhí)行上面兩行代碼的線程所執(zhí)行的。想要讓創(chuàng)建的新線程執(zhí)行 run()方法,必須調(diào)用新線程的 start 方法。

線程名

當(dāng)創(chuàng)建一個(gè)線程的時(shí)候,可以給線程起一個(gè)名字。它有助于我們區(qū)分不同的線程。例如:如果有多個(gè)線程寫入 System.out,我們就能夠通過(guò)線程名容易的找出是哪個(gè)線程正在輸出。例子如下:

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());

需要注意的是,因?yàn)?MyRunnable 并非 Thread 的子類,所以 MyRunnable 類并沒有 getName()方法??梢酝ㄟ^(guò)以下方式得到當(dāng)前線程的引用:

Thread.currentThread();

因此,通過(guò)如下代碼可以得到當(dāng)前線程的名字:

String threadName = Thread.currentThread().getName();

線程代碼舉例:

這里是一個(gè)小小的例子。首先輸出執(zhí)行main()方法線程名字。這個(gè)線程 JVM 分配的。然后開啟 10 個(gè)線程,命名為 1~10。每個(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();
      }
  }
}

需要注意的是,盡管啟動(dòng)線程的順序是有序的,但是執(zhí)行的順序并非是有序的。也就是說(shuō),1 號(hào)線程并不一定是第一個(gè)將自己名字輸出到控制臺(tái)的線程。這是因?yàn)榫€程是并行執(zhí)行而非順序的。Jvm 和操作系統(tǒng)一起決定了線程的執(zhí)行順序,他和線程的啟動(dòng)順序并非一定是一致的。

上一篇:CAS下一篇:多線程的優(yōu)點(diǎn)