1)線程包括哪些狀態,分別是什么?
1.新建狀態(new) :線程對象被創建后,就進入了新建狀態。例如,Thread thread = new Thread()
2.就緒狀態(Runnable):也被稱為“可執行狀態”。線程對象被創建后,其它線程調用了該對象的start()方法,從而來啟動該線程。例如,thread.start()。處於就緒狀態的線程,隨時可能被CPU調度執行
3.運行狀態(Running):線程獲取CPU權限進行執行。需要注意的是,線程只能從就緒狀態進入到運行狀態
4.阻塞狀態(Blocked): 阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
(01) 等待阻塞 -- 通過調用線程的wait()方法,讓線程等待某工作的完成。
(02) 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態。
(03) 其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢
5.死亡狀態(dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
2)實現多線程的方式
1.繼承Thread類,重寫run()方法
Thread本質上也是實現了Runnable接口的一個實例,它代表了一個線程的實例,並且,啟動線程的唯一方法就是通過Thread類的start()方法
代碼實現:
public class MyThread extends Thread { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(this.getName() + " 賣票:ticket" + this.ticket--); } } } public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); MyThread thread3 = new MyThread(); thread1.start(); thread2.start(); thread3.start(); } }
運行結果:
Thread-1 賣票:ticket10 Thread-1 賣票:ticket9 Thread-1 賣票:ticket8 Thread-1 賣票:ticket7 Thread-1 賣票:ticket6 Thread-1 賣票:ticket5 Thread-1 賣票:ticket4 Thread-1 賣票:ticket3 Thread-1 賣票:ticket2 Thread-1 賣票:ticket1 Thread-0 賣票:ticket10 Thread-0 賣票:ticket9 Thread-0 賣票:ticket8 Thread-0 賣票:ticket7 Thread-2 賣票:ticket10 Thread-0 賣票:ticket6 Thread-2 賣票:ticket9 Thread-2 賣票:ticket8 Thread-2 賣票:ticket7 Thread-2 賣票:ticket6 Thread-2 賣票:ticket5 Thread-2 賣票:ticket4 Thread-2 賣票:ticket3 Thread-2 賣票:ticket2 Thread-2 賣票:ticket1 Thread-0 賣票:ticket5 Thread-0 賣票:ticket4 Thread-0 賣票:ticket3 Thread-0 賣票:ticket2 Thread-0 賣票:ticket1
2.實現Runnable接口,並實現run()方法
以下是主要步驟:
1)自定義類並實現Runnable接口,實現run()方法。
2)創建Thread對象,用實現Runnable接口的對象作為參數實例化該Thread對象
3)調用Thread的start()方法
代碼實現:
public class MyThread implements Runnable { private int ticket = 10; public void run() { for (int i = 0; i < 20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName() + " 賣票:ticket" + this.ticket--); } } } public static void main(String[] args) { MyThread myThread=new MyThread(); Thread thread1=new Thread(myThread); Thread thread2=new Thread(myThread); Thread thread3=new Thread(myThread); thread1.start(); thread2.start(); thread3.start(); } }
運行結果:
Thread-0 賣票:ticket9 Thread-1 賣票:ticket10 Thread-2 賣票:ticket8 Thread-1 賣票:ticket6 Thread-0 賣票:ticket7 Thread-1 賣票:ticket4 Thread-2 賣票:ticket5 Thread-1 賣票:ticket2 Thread-0 賣票:ticket3 Thread-2 賣票:ticket1
說明:運行結果表示一共賣出了10張票,說明這三個線程共享了Runnable接口
3.實現Callable接口,重寫call()方法(這種方法不常用)
Callable接口實際是屬於Executor框架中的功能類...
3)run()方法和start()方法有什么區別
start()方法:作用為啟動一個線程,啟動后該線程處於就緒狀態,而非運行狀態,也就意味着這個線程可以被JVM來調度執行。
在調度過程中,JVM通過調用線程類的run()方法來完成實際的操作,當run()方法結束后,線程就會終止。
run()方法:如果直接調用線程類的run()方法,會被當做一個普通函數調用,程序中仍然只有主線程這一個線程,並不會產生新的線程!
總的來說:start()方法能夠異步地調用run()方法,但是直接調用run()方法卻是同步的,無法達到多線程的目的
代碼示例:
public class ThreadDemo extends Thread{ @Override public void run() { System.out.println("ThreadDemo:begin"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadDemo:end"); } }
public class StartAndRunTest { /* test1調用start()方法 */ public static void test1(){ System.out.println("test1:begin"); Thread t1=new ThreadDemo(); t1.start(); System.out.println("test1:end"); } /* test2調用run()方法 */ public static void test2(){ System.out.println("test2:begin"); Thread t1=new ThreadDemo(); t1.run(); System.out.println("test2:end"); } public static void main(String[] args) { test1(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); test2(); } }
運行結果:
從test1的運行結果來看,線程t1是在test1方法結束后才執行的,不需要等待t1,start()運行結束后(
System.out.println("ThreadDemo:end")
)就可以執行,說明在test1中調用start()是新開啟了一個線程,main線程和t1線程是異步執行的
從test2的執行結果來看,調用t1.run()是同步的調用,語句System.out.println("ThreadDemo:end")必須等t1.run()方法調用結束后
才能 執行
4)sleep()方法與wait()方法的區別
1.原理不同。sleep()方法是Thread類的靜態方法,是線程用來控制自身流程的,它會使此線程暫停執行一段時間,而把執行機會讓給
其他線程,等到計時時間一到,此線程會自動“蘇醒”。
而wait()方法是Object類的方法,用於線程間的通信,這個方法會使當前擁有該對象鎖的進程等待,wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前的線程。
2.對鎖的處理機制不同。調用sleep()方法不會釋放鎖,而調用wait()方法會釋放它所占用的鎖,從而使線程所在對象中的其他synchronized數據可被別的線程使用。
3.使用區域不同。由於wait()方法的特殊意義,因此它必須放在同步控制方法或者同步語句塊中使用,而sleep()方法則可以放在任何地方使用
還有就是sleep()方法必須捕獲異常,而wait()方法不需要