java中,啟動線程通常是通過Thread或其子類通過調用start()方法啟動。
常見使用線程有兩種:實現Runnable接口和繼承Thread。而繼承Thread亦或使用TimerTask其底層依舊是實現了Runnabel接口。考慮到java的單繼承的限制,所以在開發過程中大部分情況在使用線程的時候是通過實現Runnabel接口或者Runnbel匿名類來實現的。
例如:
package com.zpj.thread.blogTest; /** * Created by PerkinsZhu on 2017/8/11 16:42. */ public class ThreadTest { public static void main(String[] args) { ThreadTest threadTest = new ThreadTest(); //所有的這些啟動方式的執行體都是在run方法中完成的 threadTest.testLambda(); threadTest.testRunnableWithAnonymousRunnable(); threadTest.testRunnableWithAnonymousThread(); threadTest.testRunnable(); threadTest.testMyThread(); } public void testLambda() {//lambda表達式開啟線程 jdk1.8中的特性 new Thread(() -> System.out.println("i am lambda Thread....")).start(); } public void testRunnableWithAnonymousThread() {//匿名Thread類開啟線程 new Thread() { @Override public void run() { System.out.println("i am ThreadWithAnoymous"); } }.start(); } public void testRunnableWithAnonymousRunnable() {//匿名Runnable類開啟線程 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("i am RunableWithAnoymous"); } }); thread.start(); } public void testRunnable() {//實現Runnable接口 MyRunnable runable = new MyRunnable(); Thread thread = new Thread(runable); thread.start(); } public void testMyThread() {//繼承自Thread MyThread thread = new MyThread(); thread.setName("MyThread"); thread.start(); } } class MyRunnable implements Runnable {//實現Runnable接口 @Override public void run() { System.out.println("i am MyRunnable"); } } class MyThread extends Thread {//繼承Thread @Override public void run() { System.out.println(" i am MyThread!!"); } }
注意,直接調用run()方法的方式執行線程體並未開啟新線程,只是在main方法中調用了一個普通方法而已。而使用start()方法則會開啟一個新的線程執行。兩者的區別主要表現在前者是阻塞式的,而后者為非阻塞式。
例如:
public void testStartAndRun(){ MyThread thread = new MyThread(); thread.setName("MyThread"); thread.start();//start啟動兩者異步非阻塞運行 while (true){ System.out.println("---- "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread extends Thread {//繼承Thread @Override public void run() { while(true){ System.out.println("=== "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
運行結果如下:
---- main
==== MyThread
---- main
==== MyThread
==== MyThread
---- main
==== MyThread
---- main
而把thread.start() 修改為thread.run();之后,則如下
==== main ==== main ==== main ==== main ==== main ==== main
這里之所以線程名稱為main是因為是在main線程中調用的run方法,所以打印出來的是===main。而thread.run();語句下面的循環則永遠不會執行,程序將會一直在run方法中循環下去。
那么調用start方法,程序都做了些什么呢?看一下底層實現。
public synchronized void start() { //驗證線程的狀態 if (threadStatus != 0) {//這里的驗證涉及到線程不能重復啟動的問題,線程多次調用start則會拋出該異常 throw new IllegalThreadStateException(); } //把該線程加入到線程組中 group.add(this); boolean started = false;//標識線程是否啟動成功 try { start0();//調用native方法啟動線程 該方法是阻塞的,程序等待其完成之后執行下面語句如果執行失敗則直接拋出異常進入finally。 started = true;//修改線程啟動狀態 } finally { try { if (!started) {//啟動失敗,則移出線程組 group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0();//native方法啟動線程
通過Runnable啟動具有一定的局限,執行線程沒有返回值,無法捕獲異常。在有些特殊情況下需要線程返回結果的時候就不太合適。這時可以選擇Callable接口來完成。Callable涉及到Future模型,這到后面再說。
=============================================
原文鏈接:多線程(三) java中線程的簡單使用 轉載請注明出處!
=============================================
-----end