【多執行緒高併發程式設計】二 實現多執行緒的幾種方式

程式猿學社發表於2020-05-08

程式猿學社的GitHub,歡迎Star
https://github.com/ITfqyd/cxyxs
本文已記錄到github,形成對應專題

前言:

上章,我們已經瞭解執行緒的一些基本概念。本文我們來看看多執行緒的應用場景,為什麼要用多執行緒,以及實現一個多執行緒有幾種方式。

1.什麼是多執行緒?

多執行緒是指通過軟體優化和硬體(CPU)的方式,同時併發執行多個執行緒(任務)。更好的執行系統的資源。

例如,社長,很久以前,接到boss的提的一個業務,需要開發一個充電樁管理物聯網管理平臺,實現通過網站,檢視各個充電樁的情況。如果就社長一個人開發,感覺1年搞定都有點難,畢竟社長專注於後端開發,這時社長就跟boss提出,需要增加人馬,招一個前端,一個後端。社長就負責跟硬體對接,每個人負責一塊,各種同步開發。最後,通過社長三人的努力,半年就交差了。這就是多執行緒的好處。多個人,資訊也是共享(一個程式內的多個執行緒,資源是共享在同一個記憶體中)
在這裡插入圖片描述

1.1應用場景

  • 網站傳送多個請求,會一一返回結果,也就是很高的使用了多執行緒。如果沒有多執行緒,我們搶票,就得發一個請求後,需要等請求處理完後,才能執行。
  • 扣扣聊天介面,如果沒有多執行緒,發一個訊息,需要上一個訊息處理完後,才能處理下一個需求。
  • 通過netty解析資料包文,如果沒有多執行緒,1w個執行緒,直接懟進來,我們只能一個個處理,肯定處理不過來,如果沒有多執行緒,解析邏輯也無法和業務邏輯分離開,實現程式的解耦。

2.實現一個多執行緒的常見幾種方式

為了模擬真實的場景,每個執行緒中,都增加了延遲執行的程式碼。

Thread.sleep(1000);

這句程式碼表示休眠1秒鐘,以毫秒為單位。

通過繼承的方式,實現多執行緒(第一種)

package com.cxyxs.two;

import java.util.Date;

/**
 * Description:第一種:通過繼承的方式,實現多執行緒
 *  轉發請註明來源  程式猿學社 - https://ithub.blog.csdn.net/
 * Author: 程式猿學社
 * Date:  2020/2/17 21:37
 * Modified By:
 */
public class MyThreadExtend extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("程式猿學社:社長在開發中,通過繼承的方式實現:" + new Date());
        }
    }
}

呼叫程式碼

  //第一種方式
  MyThreadExtend threadExtend = new MyThreadExtend();
  threadExtend.start();
  • 因為java中是單繼承,所以不推薦通過這種方式實現多執行緒。如果該類已經被繼承,是無法繼承Thread類的。

通過實現runnable介面,實現多執行緒(第二種)

package com.cxyxs.two;

import java.util.Date;

/**
 * Description:轉發請註明來源  程式猿學社 - https://ithub.blog.csdn.net/
 * Author: 程式猿學社
 * Date:  2020/2/17 21:43
 * Modified By:
 */
public class MyThreadRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("小二在開發中,通過實現Runnable介面方式實現:" + new Date());
        }
    }
}

呼叫程式碼

//第二種方式
MyThreadRunnable runnable = new MyThreadRunnable();
Thread thread = new Thread(runnable);
thread.start();

匿名內部類實現(第三種)

Runnable方式

//匿名內部類-第一種
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("通過匿名內部類的方式第一種實現!");
            }
        }).start();

繼承類方式

//匿名內部類-第二種
        new Thread(){
            @Override
            public void run() {
                System.out.println("通過匿名內部類的方式第二種實現!");
            }
        }.start();

通過Callable實現多執行緒(第四種)

public class MyThreadCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sum+=i;
            System.out.println("小王通過實現Callable介面的方式實現:" + new Date());
        }
        return sum;

    }
}

呼叫程式碼

//第四種 通過Callable實現多執行緒
        MyThreadCallable callable = new MyThreadCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(callable);
        new Thread(result).start();
        try {
            Integer sum =  result.get();
            System.out.println("計算結果:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
  • 之前幾種實現多執行緒的方法,需要重寫run方法,啟動多執行緒。而Callable方式實現多執行緒,需要重寫call方法。通過FutureTask包裝器來建立Thread執行緒
  • 通過呼叫get方法獲取多執行緒的執行結果。注意,get方法會一直堵塞,沒有返回值,主執行緒會一直等待。
  • 應用場景,例如批量大資料的匯出,假設我們要匯出100w資料,需要30s,我們就可以通過分頁,每個執行緒查10w的資料,啟動10個執行緒,來獲取處理結果。這樣就可以通過多執行緒提供查詢的效率。

啟動一個main執行緒,啟動了幾個執行緒

 public class ThreadCount {
    public static void main(String[] args) {
        ThreadGroup group =
                Thread.currentThread().getThreadGroup();
        int count = group.activeCount();
        group.list();
    }
}

idea列印
在這裡插入圖片描述
eclipse列印
在這裡插入圖片描述
啟動了2個執行緒,一個主執行緒main,是程式的入口。
還有一個[Monitor Ctrl-Break,這是IDEA特有的監控執行緒。正確的列印應該是gc執行緒。通過上面兩張圖,就可以得出這個結論。

多個執行緒進行測試

public class Test {
    public static void main(String[] args) {
        //第一種方式
        MyThreadExtend threadExtend = new MyThreadExtend();
        threadExtend.start();

        //第二種方式
        MyThreadRunnable runnable = new MyThreadRunnable();
        Thread thread = new Thread(runnable);
        thread.start();

        //第三種方式
        //匿名內部類-第一種
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("通過匿名內部類的方式第一種實現!");
            }
        }).start();

        //匿名內部類-第二種
        new Thread() {
            @Override
            public void run() {
                System.out.println("通過匿名內部類的方式第二種實現!");
            }
        }.start();

        //第四種 通過Callable實現多執行緒
        MyThreadCallable callable = new MyThreadCallable();
        FutureTask<Integer> result = new FutureTask<Integer>(callable);
        new Thread(result).start();
        try {
            Integer sum =  result.get();
            System.out.println("計算結果:"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

在這裡插入圖片描述

  • 通過測試的結果,我們發現,資料是交換執行的,如果是單cpu,每次只能執行一個通道,就算用多執行緒的方式實現,每次也是交替執行。只是計算機執行速度很快,我們看不出什麼區別。說到這裡,就有社友提出疑問,單cpu,還有必要用多執行緒嗎
    就算是單cpu,利用多執行緒也有很多好處。讓我們可以減少沒必要的等待,更好的利用資源。
  • 如果是多cpu,就可以同時併發執行多個任務,大大的提高執行效率。

相關文章