執行緒(thread)是一個程式內部的一條執行路徑,我們所熟悉的main方法其實就是一條單獨執行路徑,若程式中只有一條執行路徑那麼這個程式就是單線程程式;既然有單線程,那麼也相對的會有多線程,字面意思可以理解為“相對單線程從軟硬體上多條執行流程的技術”,多線程的好處是提高CPU的利用率。在多執行緒程式中,一個執行緒必須等待的時候,CPU可以運行其它的執行緒而不是等待,大大提高程式的效率。
方式一建立過程:
定義一個子類別MyThread繼承線程類別java.lang.Thread,重寫run()方法;
建立MyThread類別的物件;
呼叫線程物件的start()方法啟動執行緒(啟動後仍執行run()方法);
public class ThreadDemo01 { public static void main(String[] args) { MyThread myThread1 = new MyThread(); myThread1.start(); for (int i = 0; i < 3; i++) { System.out.println("主线程正在执行~~"); } } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println("子线程正在执行~~"); } } } //输出结果(不唯一): //主线程正在执行~~ //主线程正在执行~~ //主线程正在执行~~ //子线程正在执行~~ //子线程正在执行~~ //子线程正在执行~~
#在上述程式碼中共有兩個執行緒在執行,分別是main方法的主執行緒和執行緒物件mythread呼叫start()啟動的子執行緒。不過輸出結果為什麼會不唯一的呢?原因是因為兩個執行緒在執行過程中會發生CPU的搶佔,誰先搶到誰將優先執行。
那我們為什麼不直接使用執行緒物件呼叫run()方法呢?若直接呼叫run()則只是普通的呼叫方法,即單線程,而start()方法則是用來啟動的子線程的,由此才能出現多線程。
方式一優缺點:
優點:編碼簡單;
#缺點:執行緒類別已經繼承Thread,無法繼承其他類,不利於擴充;
#方式二建立過程:
1、定義一個執行緒任務類別MyRunnable實作Runnable接口,重寫run()方法;
2、建立MyRunnable物件;
3、把MyRunnable任務物件交給Thread處理;
4、呼叫線程物件的start()方法啟動執行緒;
Thread建構器 | 方法 |
public Thread (String name) | 可以為目前執行緒指定名稱 |
public Thread (Runnable target) | 封裝Runnable物件成為執行緒物件 |
#public Thread (Runnable target ,String name) | 封裝Runnable物件成為執行緒對象,並指定執行緒名稱 |
public class ThreadDemo02 { public static void main(String[] args) { MyRunnable target = new MyRunnable(); Thread thread = new Thread(target); thread.start(); for (int i = 0; i < 3; i++) { System.out.println("主线程正在执行~~"); } } } class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println("子线程正在执行~~"); } } } //输出结果(不唯一): //主线程正在执行~~ //子线程正在执行~~ //子线程正在执行~~ //子线程正在执行~~ //主线程正在执行~~ //主线程正在执行~~
該程式碼與方式一的不同之處在於需要將MyRunnable任務物件封裝在Thread中,其他的地方基本上是沒有變化的。
方式二優缺點:
優點:線程任務類別只是實作接口,可以繼續繼承類別和實作接口,擴展性強;
缺點:程式設計多一層物件包裝,如果執行緒有執行結果是不可以直接回傳的。
接下來我們同樣使用實作Runnable介面(匿名內部類別形式)來實作多執行緒的建立:
1、建立Runnable匿名內部類別物件;
2、交給Thread處理;
3、呼叫線程物件的start()啟動線程;
//正常版: public class ThreadDemo01 { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println("子线程正在执行~~"); } } }); thread.start(); for (int i = 0; i < 3; i++) { System.out.println("主线程正在执行~~"); } } } //lambda简化版: new Thread(()-> { for (int i = 0; i < 3; i++) { System.out.println("子线程正在执行~~"); } }).start();
該種方法本質上其實並沒有太大的區別只不過是一個需要創建線程對象,而另一個則是透過匿名內部類別實現的多執行緒。而該區塊程式碼也可以透過lambda表達式進行精簡,不知道大家是否還對這個知識點有印象呢?若忘記了可以看一下這篇文章:Java中的lambda表達式如何理解-精簡
在學習過前面兩種建立多執行緒的方式以後,我們會發現有一個問題:1、重寫的run()方法不能直接傳回結果;2、不適合需要傳回執行緒執行結果的業務場景。因此,我們需要第三種方式來解決這些問題。
方式三創建過程:
1、定義類別實作Callable接口,重寫call()方法,封裝要做的事情;
2、用FutureTask把Callable物件封裝成執行緒任務物件;
3、把執行緒任務物件交給Thread處理;
#4、呼叫Thread的start()方法啟動線程,執行任務;
5、執行緒執行完畢後,透過FutureTask的get()方法取得任務執行的結果。
方法名稱 | 說明 | |
#public FutureTask<>(Callable call) | #把Callable物件封裝成FutureTask物件 | |
取得執行緒執行call方法傳回的結果 |
方式 | 优点 | 缺点 |
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他的类,不能返回线程执行的结果 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类 | 编程相对复杂,不能返回线程执行的结果 |
实现Callable接口 | 扩展性强,实现该接口的同时还可以继承其他的类,可以得到线程的执行结果 | 编程相对复杂 |
方法名称 | 说明 |
String getName() | 获取当前线程的名称,默认线程名称是Thread-索引 |
void setName(String name) | 将此线程更改为指定的名称,通过构造器也可以设置线程名称 |
简单地通过一段代码让大家能够清晰地了解这个代码该如何使用:
public class ThreadDemo04 { public static void main(String[] args) throws Exception { thread thread1 = new thread(); thread1.setName("1号子线程"); thread1.start(); thread thread2 = new thread(); thread2.setName("2号子线程"); thread2.start(); } } class thread extends Thread { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(this.getName()+"正在执行任务"+i); } } } //输出结果: //2号子线程正在执行任务0 //1号子线程正在执行任务0 //2号子线程正在执行任务1 //1号子线程正在执行任务1 //2号子线程正在执行任务2 //1号子线程正在执行任务2
方法名称 | 说明 |
public static void sleep(long time) | 让当前线程休眠指定的时间后再继续执行,单位为毫秒 |
public class ThreadDemo05 { public static void main(String[] args) throws Exception { for (int i = 0; i < 5; i++) { System.out.println(i); if (i==3){ Thread.sleep(5000); } } } } //输出结果: //1 //2 //3 //在输出过3以后,等待5秒之后再进行输出 //4
以上是java中的線程怎麼理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!