Java十分鐘入門多線程上篇

什麼是多線程?

在學習前,我們先對程序、進程、線程、並行、並發有個基礎的概念瞭解:

  • 程序: 為完成指定任務,用編程語言編寫的一組指令的集合,即指一段靜態的代碼,靜態對象。
  • 進程: 是程序的一次執行過程,是一個動態的過程,進程自身有產生、使用和消亡的過程。(也稱為生命周期,在後面會介紹)
  • 線程: 進程可進一步細化為線程,是一個程序內部的一條執行路徑,也就是進程內有線程
  • 並行: 指兩個或者多個事件在同一時刻發生,(同時發生)
  • 並發: 指兩個或者多個事件在同一個時段內發生,(並不是同時發生)

更好的理解進程和線程:

打開計算機任務管理器:

多線程有什麼用?

我們就不看其他長篇大論,簡單的說就是:節省時間,提高效率(在CPU滿足的情況下同時執行多個任務)

舉個例子:

12306大傢肯定很熟悉,在高峰期的時候,會有幾十萬的用戶同時在瀏覽和購票,假如你是第1000個進入購票系統的,系統不會讓你等待前面999人買完你才可以買,不管你什麼時候加入購票系統,隨時都可以買,這就是多線程。而且CPU的運算速度讓你感覺不用等待延遲(在CPU處理負載能力之內)。

多線程優點小結:

  • 提高瞭用戶的體驗度,使程序的相應速度更快
  • 占用大量的處理時間的任務可以使用多線程來提高CPU的使用效率
  • 可以設優先級來優化性能

線程的生命周期:

線程的生命周期包括5個階段: 新建、就緒、運行、阻塞、銷毀

  • 新建:在編譯期間創建線程對象就屬於新建狀態,如Thread t1=new Thred()。(在下面介紹如何創建)
  • 就緒: 當一個線程對象調用瞭start()方法就屬於就緒狀態,這時候線程處於等待CPU執行此線程的任務, 誰先搶到cpu資源就誰先執行,是隨機的。
  • 運行: 就緒狀態的線程獲得CPU資源執行的任務的時候就是運行狀態,run()方法用於執行線程操作
  • 阻塞狀態:在運行狀態的時候,可能因為某些原因導致運行狀態的線程變成瞭阻塞狀態不能再繼續執行任務, 這個時候其他線程獲取CPU資源的可能性就更大瞭,CPU並不會等待阻塞狀態的線程(任務)而去執行其他線程 的任務,也可以通過線程調度的機制“線程休眠”讓運行狀態下的線程任務變成阻塞,或者讓阻塞狀態的線 程“喚醒”變成就緒狀態。直到再次等待CPU分配資源進入到運行狀態。
  • 銷毀:如果線程正常執行完任務後或者線程被提前強制終止或者是因為出現異常導致線程任務 結束,都屬於銷毀狀態。

關於阻塞狀態的進一步理解:

假如你去超市買東西(創建),選完東西準備結賬(就緒),你在付款瞭,但是的時候發現錢不夠瞭(運行),不能完成支付,需要打電話給朋友借一點,因為你沒有辦法支付瞭(這就進入的阻塞狀態),就不能進入下一步驟(走出超市,對應銷毀)。

註意: 進入阻塞狀態後並不是說不管你瞭,而是準備好後再次讓CPU來處理你。就是你借到錢瞭,收銀員會再次讓你去付款。

創建多線程:

主線程:

當Java程序啟動的時候,一個線程立刻執行,該線程叫做主線程(main Thread),因為他是程序開始就執行的。

主線程的作用:

  • 產生其他子線程的線程
  • 必須完成最後的執行,因為它還需要執行各種關閉動作

實現多線程有5種方式:

  • 繼承Thread父類重寫run()方法,Java是單繼承如果繼承瞭Thread其他父類就不能再繼承。(這種方式不建議)
  • 實現Runable接口重寫run()方法。(最常用)
  • 實現Callable接口,調用有返回值的call方法
  • 線程池創建多線程
  • 通過Java8新特性Lambda表達式實現多線程

主線程:

public class test {
    public static void main(String[] args) {
        //獲取當前線程的對象
        Thread t = Thread.currentThread();
        //輸出當前線程的名字:
        System.out.println(t.getName());
        t.setName("我是主線程");
        System.out.println(t.getName());
    }
}

//輸出:
//main
//我是主線程

通過上面的代碼可以知道當前主線程的名字,也可以修改線程名、 關於Thread的方法介紹參考官方文檔:

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html

1、繼承Thread父類重寫run()方法創建多線程:

繼承Thread重寫run方法:

//創建一個以main為主線程的子線程
public class MyThread extends Thread{
    @Override
    public void run(){
        for (int i = 1; i < 6; i++) {
            System.out.println("我是子線程:"+i);
        }
    }
}

測試類:

public class test {
    public static void main(String[] args) {
        
        //創建子線程對象(新建狀態)
        MyThread t = new MyThread();
        //就緒狀態,等待CPU執行該線程任務
        t.start();
        for (int i = 1; i < 6; i++) {
            System.out.println("main任務:主線程執行:"+i);
        }
    }
}

結果:

2、實現Runable接口重寫run()方法(最常用)

創建子線程:

public class MyThread implements Runnable{

    @Override
    public void run(){
        for (int i = 1; i < 10; i++) {
            System.out.println("子線程-"+Thread.currentThread().getName()+i);
        }
    }
}

測試類:

public class test {
    public static void main(String[] args) {

        //創建子線程對象(新建狀態)
        MyThread t1 = new MyThread();
        t1.setName("我是A線程:");
        MyThread t2 = new MyThread();
        t2.setName("我是B線程:");
        MyThread t3 = new MyThread();
        t3.setName("我是C線程:");

        //就緒狀態,等待CPU執行該線程任務
        t1.start();
        t2.start();
        t3.start();
    }
}

結果:

3、實現Callable<?>接口實現有返回值的多線程

public class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        try{
            //因為是泛型接口,call實 現瞭線程任務,並且返回值可以自定義
            System.out.println("實現瞭Callable接口重寫call方法");
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

測試類:

public class test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //創建對象:
        MyThread myThread = new MyThread();
        //通過線程池幫你創建瞭2個線程對象
        //ExecutorService是Java中對線程池定義的一個接口,它java.util.concurrent包中
        ExecutorService service = Executors.newFixedThreadPool(2);
        //通過線程池對象調用submit方法,提交執行call方法的線程任務,最後返回結果
        Future<Boolean> result1 = service.submit(myThread);
        Future<Boolean> result2 = service.submit(myThread);

        boolean r1= result1.get();
        boolean r2= result2.get();

輸出:

實現瞭Callable接口重寫call方法
實現瞭Callable接口重寫call方法
我是子線程1,返回的結果:true
我是子線程2,返回的結果:true

其他兩個創建多線程的方式不怎麼用,就不一一介紹,大傢也可以看看其他技術博客學習~~

小結:

這篇文章主要將瞭多線程的概念以及創建多線程的方式,多線程還有很多的內容,就在下一篇再繼續介紹啦!感謝閱讀!

到此這篇關於Java十分鐘入門多線程上篇的文章就介紹到這瞭,更多相關Java 多線程內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: