中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java多線程的知識點有哪些

發布時間:2022-02-23 15:53:16 來源:億速云 閱讀:115 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“Java多線程的知識點有哪些”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java多線程的知識點有哪些”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

一、基本概念:線程、進程

1.1、進程與線程的具體介紹

線程(thread),進程可進一步細化為線程,是一個程序內部的一條執行路徑。

  • 若一個進程同一時間并行執行多個線程,就是支持多線程的

  • 線程作為調度和執行的單位,每個線程擁有獨立的運行棧和程序計數器(pc),線程切換的開銷小

  • 一個進程中的多個線程共享相同的內存單元/內存地址空間à它們從同一堆中分配對象,可以訪問相同的變量和對象。這就使得線程間通信更簡便、高效。但多個線程操作共享的系統資源可能就會帶來安全的隱患。

進程(process),是程序的一次執行過程,或是正在運行的一個程序。是一個動態的過程:有它自身的產生、存在和消亡的過程。——生命周期

  • 運行中的QQ,運行中的MP3播放器

  • 程序是靜態的,進程是動態的進程作為資源分配的單位,系統在運行時會為每個

  • 進程分配不同的內存區域

1.2、對于CPU而言的理解

單核CPU和多核CPU的理解

  • 單核CPU,其實是因為在一個時間單元內,也只能執行一個線程的任務。例如:雖然有多車道,但是一種假的多線程,收費站只有一個工作人員在收費,只有收了費才能通過,那么CPU就好比收費人員。如果有某個人不想交錢,那么收費人員可以把他“掛起”(晾著他,等他想通了,準備好了錢,再去收費)。但是因為CPU時間單元特別短,因此感覺不出來。

  • 如果是多核的話,才能更好的發揮多線程的效率。(現在的服務器都是多核的)

  • 一個 Java 應用程序 java.exe,其實至少有三個線程:main() 主線程,gc() 垃圾回收線程,異常處理線程。當然如果發生異常,會影響主線程。

并行與并發

  • 并行:多個 CPU 同時執行多個任務。比如:多個人同時做不同的事。

  • 并發:一個 CPU (采用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事。

1.3、為什么要使用多線程

  • 背景:

以單核CPU 為例,只使用單個線程先后完成多個任務(調用多個方法),肯定比用多個線程來完成用的時間更短,為何仍需多線程呢?

多線程程序的優點:

  • 提高應用程序的響應。對圖形化界面更有意義,可增強用戶體驗。

  • 提高計算機系統 CPU 的利用率

  • 改善程序結構。將既長又復雜的進程分為多個線程,獨立運行,利于理解和修改

二、線程的創建與使用

2.1、如何去創建和啟動一個線程

  • Java語言的 JVM 允許程序運行多個線程,它通過 java.lang.Thread 類來體現。

  • Thread 類的特性:

每個線程都是通過某個特定 Thread 對象的 run() 方法來完成操作的,經常把 run() 方法的主體稱為線程體通過該 Thread 對象的 start() 方法來啟動這個線程,而非直接調用 run()

2.2、Thread類的具體分析

構造器:

Thread():創建新的 Thread 對象
Thread(String threadname):創建線程并指定線程實例名
Thread(Runnable target):指定創建線程的目標對象,它實現了 Runnable 接口中的 run 方法
Thread(Runnable target, String name):創建新的 Thread 對象
創建線程的兩種方式:
JDK1.5 之前創建新執行線程有兩種方法:
繼承 Thread 類的方式
實現 Runnable 接口的方式
方式一:繼承 Thread 類
定義子類繼承 Thread 類。
子類中重寫 Thread 類中的 run 方法。
創建 Thread 子類對象,即創建了線程對象。
調用線程對象 start 方法:啟動線程,調用 run 方法。

代碼演示:

* 多線程的創建,方式一:繼承于Thread類
 * 1. 創建一個繼承于Thread類的子類
 * 2. 重寫Thread類的run() --> 將此線程執行的操作聲明在run()中
 * 3. 創建Thread類的子類的對象
 * 4. 通過此對象調用start()
 * 例子:遍歷100以內的所有的偶數

//1、創建一個繼承于Thread類的子類
class Thread01 extends Thread {
    //2、重寫Thread類的run()
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        //3、創建Thread類的子類的對象
        Thread01 thread01 = new Thread01();
        //4.通過此對象調用start()
        thread01.start();

        //創建第二個線程
        Thread01 thread02 = new Thread01();

        //注意:這里我們不能直接手動調用 run()方法
        //thread01.run();

        //注意:當我們再次調用start()時會直接報錯:IllegalThreadStateException,所以一個線程只能用一次
        //thread01.start();

        thread02.start();


        //當前操作仍然是在main線程中執行
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "主線程被執行了");
            }

        }
    }
}

方式二:實現 Runnable 接口

  • 定義子類,實現 Runnable 接口。

  • 子類中重寫 Runnable 接口中的 run 方法。

  • 通過 Thread 類含參構造器創建線程對象。

  • 將 Runnable 接口的子類對象作為實際參數傳遞給 Thread 類的構造器中。

  • 調用 Thread 類的 start 方法:開啟線程,調用 Runnable 子類接口的 run 方法。

2.3、兩種實現方式的聯系與區別

聯系:

Thread 內部其實也是實現了 Runnable 接口

Java多線程的知識點有哪些

區別:

  • 繼承 Thread: 線程代碼存放 Thread子類 run 方法中。

  • 實現 Runnable: 線程代碼存在接口的子類的 run 方法。

實現方式的好處:

  • 避免了單繼承的局限性

  • 多個線程可以共享同一個接口實現類的對象,非常適合多個相同線程來處理同一份資源。

2.4、注意事項:

  • 如果自己手動調用 run() 方法,那么就只是普通方法,沒有啟動多線程模式。

  • run() 方法由 JVM 調用,什么時候調用,執行的過程控制都有操作系統的 CPU 調度決定。

  • 想要啟動多線程,必須調用 start 方法。

  • 一個線程對象只能調用一次 start() 方法啟動,如果重復調用了,則將拋出以上的異常 IllegalThreadStateException。

2.5、代碼測試

需求:創建兩個分線程,讓其中一個線程輸出1-100之間的偶數,另一個線程輸出1-100之間的奇數。

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

        //線程一:偶數
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    if (i % 2 == 0) {
                        System.out.println(Thread.currentThread().getName() + "線程一偶數的執行數:" + i);
                    }
                }
            }
        }.start();


        //線程二:奇數
        new Thread() {
            @Override
            public void run() {

                for (int i = 0; i < 100; i++) {
                    if (i % 2 != 0) {
                        System.out.println(Thread.currentThread().getName() + "線程二奇數的執行數:" + i);
                    }
                }
            }
        }.start();
    }
}

2.6、Thread類的相關方法

void start(): 啟動線程,并執行對象的 run() 方法

run(): 線程在被調度時執行的操作String

getName(): 返回線程的名稱

void setName(String name): 設置該線程名稱

static Thread currentThread(): 返回當前線程。在 Thread 子類中就是 this,通常用于主線程和 Runnable 實現類static void yield(): 線程讓步

暫停當前正在執行的線程,把執行機會讓給優先級相同或更高的線程若隊列中沒有同優先級的線程,忽略此方法

join() : 當某個程序執行流中調用其他線程的 join() 方法時,調用線程將被阻塞,直到 join() 方法加入的 join 線程執行完為止

低優先級的線程也可以獲得執行

static void sleep(long millis):(指定時間:毫秒)

令當前活動線程在指定時間段內放棄對 CPU 控制,使其他線程有機會被執行,時間到后重排隊。

拋出 InterruptedException 異常

stop(): 強制線程生命期結束,不推薦使用

boolean isAlive(): 返回 boolean,判斷線程是否還活著

Java多線程的知識點有哪些

Java多線程的知識點有哪些

2.7、測試以上方法:

* @description: 多線程具體方法的使用
 * @date 2021/4/16 18:32
 */


class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);

                //設置線程睡眠時間
               /* try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            //當滿足當前條件時,終止線程
            /*if (i % 20 == 0) {
                yield();
            }*/


        }

    }
}

public class ThreadMethodTest {
    public static void main(String[] args) {
        MyThread thread = new MyThread();

        thread.start();
        //給主線程命名
        thread.setName("我是線程一號");
        //將分線程優先級設置成最大
        thread.setPriority(Thread.MAX_PRIORITY);

        Thread.currentThread().setName("我是主線程");
        //將主線程的優先級設置成最小
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
            }
            //join()表示:線程A中調用線程B的join()方法,那么此時線程A進入阻塞狀態,直到線程B完全執行完以后,線程A才
            //結束阻塞狀態。
            /*if (i == 20) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }*/

        }
        //isAlive():判斷當前線程是否存活
//        System.out.println("當前線程是否存活:" + thread.isAlive());


    }
}

結果:

使用 sleep() 方法時的結果
Java多線程的知識點有哪些
使用 Join方法時的結果
Java多線程的知識點有哪些

三、線程的生命周期

JDK中用Thread.State類定義了線程的幾種狀態:

要想實現多線程,必須在主線程中創建新的線程對象。Java 語言使用 Thread 類及其子類的對象來表示線程,在它的一個完整的生命周期中通常要經歷如下的五種狀態:

新建: 當一個 Thread 類或其子類的對象被聲明并創建時,新生的線程對象處于新建狀態

就緒: 處于新建狀態的線程被 start() 后,將進入線程隊列等待 CPU 時間片,此時它已具備了運行的條件,只是沒分配到 CPU 資源

運行: 當就緒的線程被調度并獲得 CPU 資源時,便進入運行狀態, run() 方法定義了線程的操作和功能

阻塞: 在某種特殊情況下,被人為掛起或執行輸入輸出操作時,讓出 CPU 并臨時中止自己的執行,進入阻塞狀態

死亡: 線程完成了它的全部工作或線程被提前強制性地中止或出現異常導致結束

生命周期結構圖

Java多線程的知識點有哪些

四、線程的同步

4.1、為什么要線程同步

  • 多個線程執行的不確定性引起執行結果的不穩定

  • 多個線程對賬本的共享,會造成操作的不完整性,會破壞數據

4.2、舉例說明:

創建三個窗口賣票,總票數為100張.使用繼承Thread類的方式

class Window extends Thread {

    private static int ticket = 100;

    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(getName() + "買票編碼號為:" + ticket);
                ticket--;
            } else {
                break;
            }
        }
    }
}

public class TicketWindowTest {
    public static void main(String[] args) {
        //創建線程
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();

        //設置線程名字
        w1.setName("窗口一:");
        w2.setName("窗口二:");
        w3.setName("窗口三:");

        //開啟線程
        w1.start();
        w2.start();
        w3.start();
    }
}

正常情況下的效果:

Java多線程的知識點有哪些

不正常的情況下的效果:

Java多線程的知識點有哪些

上段代碼出現的漏洞:

問題: 賣票過程中,出現了重票、錯票,出現了線程的安全問題

原因: 當某個線程操作車票的過程中,尚未操作完成時,其他線程參與進來,也操作車票

解決: 當一個線程 a 在操作 ticket 的時候,其他線程不能參與進來。直到線程 a 操作完 ticket 時,其他線程才可以開始操作 ticket。這種情況即使線程a出現了阻塞,也不能被改變

Java多線程的知識點有哪些

4.3、Synchronized的使用方法

Java對于多線程的安全問題提供了專業的解決方式:同步機制

 1、同步代碼塊:
    synchronized (對象){
          // 需要被同步的代碼;
    }

2、synchronized還可以放在方法聲明中,表示整個方法為同步方法。
	例如:
    public synchronized void show (String name){ 
            ….
   }

4.3、 同步機制中的鎖

同步鎖機制:

在《Thinking in Java》中,是這么說的: 對于并發工作,你需要某種方式來防止兩個任務訪問相同的資源(其實就是共享資源競爭)。 防止這種沖突的方法就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須鎖定這項資源,使其他任務在其被解鎖之前,就無法訪問它了,而在其被解鎖之時,另一個任務就可以鎖定并使用它了。

synchronized的鎖是什么?

  • 任意對象都可以作為同步鎖,所有對象都自動含有單一的鎖(監視器)

  • 同步方法的鎖: 靜態方法(類名.class)、非靜態方法(this)

  • 同步代碼塊: 自己指定,很多時候也是指定為this或類名.class

注意:

  • 必須確保使用同一個資源的多個線程共用一把鎖,這個非常重要,否則就無法保證共享資源的安全

  • 一個線程類中的所有靜態方法共用同一把鎖(類名.class),所有非靜態方法共用同一把鎖(this),同步代碼塊(指定需謹慎)

4.4、同步的范圍

如何找問題,即代碼是否存在線程安全?(非常重要)

  • 明確哪些代碼是多線程運行的代碼

  • 明確多個線程是否有共享數據

  • 明確多線程運行代碼中是否有多條語句操作共享數據

如何解決呢?(非常重要)

對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中,其他線程不可以參與執行。即所有操作共享數據的這些語句都要放在同步范圍中

注意:

  • 范圍太小: 沒鎖住所有有安全問題的代碼

  • 范圍太大: 沒發揮多線程的功能。

對于購票代碼的 bug 改進

class Windows02 implements Runnable {

    private int ticke = 100;

    @Override
    public void run() {
        while (true) {
            //使用同步代碼方式來解決線程安全問題,this:表示當前對象:【Windows02】
//            synchronized (this) {
//                if (ticke > 0) {
//                    try {
//                        Thread.sleep(100);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    System.out.println(Thread.currentThread().getName() + "購票號碼為:" + ticke);
//                    ticke--;
//                } else {
//                    break;
//                }
//            }

            show();
        }
    }

    //這里我們直接使用【同步方法】的方式來處理線程安全問題
    //在方法中加入:synchronized的效果等同上面的this,因為指代的都是當前對象,只是在同步方法中幫我們做了隱試操作。
    private synchronized void show() {
        if (ticke > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "購票號碼為:" + ticke);
            ticke--;
        }
    }
}

public class TickeWindowTest02 {
    public static void main(String[] args) {
        Windows02 windows = new Windows02();

        Thread thread = new Thread(windows);
        Thread thread01 = new Thread(windows);
        Thread thread02 = new Thread(windows);

        thread.setName("窗口一:");
        thread01.setName("窗口二:");
        thread02.setName("窗口三:");

        thread.start();
        thread01.start();
        thread02.start();


    }
}

測試結果:

Java多線程的知識點有哪些

4.5、釋放鎖的操作

  • 當前線程的同步方法、同步代碼塊執行結束。

  • 當前線程在同步代碼塊、同步方法中遇到 break、return 終止了該代碼塊、該方法的繼續執行。

  • 當前線程在同步代碼塊、同步方法中出現了未處理的 Error 或 Exception,導致異常結束。

  • 當前線程在同步代碼塊、同步方法中執行了線程對象的 wait() 方法,當前線程暫停,并釋放鎖。

4.6、不釋放鎖的操作

  • 線程執行同步代碼塊或同步方法時,程序調用 Thread.sleep()、Thread.yield() 方法暫停當前線程的執行

  • 線程執行同步代碼塊時,其他線程調用了該線程的 suspend() 方法將該線程掛起,該線程不會釋放鎖(同步監視器)。

應盡量避免使用 suspend() 和 resume() 來控制線程

4.7、線程的死鎖的問題

1、 什么事死鎖

不同的線程分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖
出現死鎖后,不會出現異常,不會出現提示,只是所有的線程都處于阻塞狀態,無法繼續。

代碼演示:

//死鎖的演示
class A {
    public synchronized void foo(B b) { //同步監視器:A類的對象:a
        System.out.println("當前線程名: " + Thread.currentThread().getName()
                + " 進入了A實例的foo方法"); // ①
//		try {
//			Thread.sleep(200);
//		} catch (InterruptedException ex) {
//			ex.printStackTrace();
//		}
        System.out.println("當前線程名: " + Thread.currentThread().getName()
                + " 企圖調用B實例的last方法"); // ③
        b.last();
    }

    public synchronized void last() {//同步監視器:A類的對象:a
        System.out.println("進入了A類的last方法內部");
    }
}

class B {
    public synchronized void bar(A a) {//同步監視器:b
        System.out.println("當前線程名: " + Thread.currentThread().getName()
                + " 進入了B實例的bar方法"); // ②
//		try {
//			Thread.sleep(200);
//		} catch (InterruptedException ex) {
//			ex.printStackTrace();
//		}
        System.out.println("當前線程名: " + Thread.currentThread().getName()
                + " 企圖調用A實例的last方法"); // ④
        a.last();
    }

    public synchronized void last() {//同步監視器:b
        System.out.println("進入了B類的last方法內部");
    }
}

public class DeadLock implements Runnable {
    A a = new A();
    B b = new B();

    public void init() {
        Thread.currentThread().setName("主線程");
        // 調用a對象的foo方法
        a.foo(b);
        System.out.println("進入了主線程之后");
    }

    public void run() {
        Thread.currentThread().setName("副線程");
        // 調用b對象的bar方法
        b.bar(a);
        System.out.println("進入了副線程之后");
    }

    public static void main(String[] args) {
        DeadLock dl = new DeadLock();
        new Thread(dl).start();
        dl.init();
    }
}

測試結果:概率性的出現

Java多線程的知識點有哪些

2、解決方法

  • 專門的算法、原則

  • 盡量減少同步資源的定義盡量

  • 避免嵌套同步

3、什么是Lock鎖

  • 從 JDK 5.0 開始,Java 提供了更強大的線程同步機制——通過顯式定義同步鎖對象來實現同步。同步鎖使用 Lock 對象充當。

  • java.util.concurrent.locks.Lock 接口是控制多個線程對共享資源進行訪問的工具。鎖提供了對共享資源的獨占訪問,每次只能有一個線程對 Lock 對象加鎖,線程開始訪問共享資源之前應先獲得 Lock 對象。

  • ReentrantLock 類實現了 Lock ,它擁有與 synchronized 相同的并發性和內存語義,在實現線程安全的控制中,比較常用的是 ReentrantLock,可以顯式加鎖、釋放鎖。

4、具體如何使用:

Java多線程的知識點有哪些

5、synchronized 與 Lock 鎖有何區別

Lock 是顯式鎖(手動開啟和關閉鎖,別忘記關閉鎖),synchronized 是隱式鎖,出了作用域自動釋放

Lock 只有代碼塊鎖,synchronized 有代碼塊鎖和方法鎖

使用 Lock 鎖,JVM將花費較少的時間來調度線程,性能更好。并且具有更好的擴展性(提供更多的子類)

優先使用順序:

Lock →同步代碼塊(已經進入了方法體,分配了相應資源) → 同步方法(在方法體之外)

五、線程的通信

5.1、方法介紹與注意事項:

wait() 與 notify() 和 notifyAll()

wait(): 令當前線程掛起并放棄 CPU、同步資源并等待,使別的線程可訪問并修改共享資源,而當前線程排隊等候其他線程調用 notify()或notifyAll() 方法喚醒,喚醒后等待重新獲得對監視器的所有權后才能繼續執行。

notify(): 喚醒正在排隊等待同步資源的線程中優先級最高者結束等待

notifyAll (): 喚醒正在排隊等待資源的所有線程結束等待.

注意事項:

這三個方法只有在 synchronized 方法或 synchronized 代碼塊中才能使用,否則會報 java.lang.IllegalMonitorStateException 異常。

因為這三個方法必須有鎖對象調用,而任意對象都可以作為 synchronized 的同步鎖,因此這三個方法只能在 Object 類中聲明。

sleep() 和 wait() 有何不同之處:

相同點: 一旦執行方法,都可以使得當前的線程進入阻塞狀態。

不同點:

兩個方法聲明的位置不同: Thread 類中聲明 sleep() , Object 類中聲明 wait()

調用的要求不同: sleep() 可以在任何需要的場景下調用。 wait() 必須使用在同步代碼塊或同步方法中

關于是否釋放同步監視器: 如果兩個方法都使用在同步代碼塊或同步方法中,sleep() 不會釋放鎖,wait()會釋放鎖。

案例一:

使用兩個線程打印 1-100。線程1, 線程2 交替打印

class Number implements Runnable {
    private int number = 1;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                //因為現在使用的是當前對象,所以前面省略this.
                //如果使用的是其他對象,那么就用對象.的方式去調用該方法
                notify();
                if (number <= 100) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " :" + "打印數為" + number);
                    number++;
                    try {
                        //調用該方法時,線程進入阻塞狀態
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}
public class CommunicationTest {
    public static void main(String[] args) {
        Number number = new Number();
        Thread thread = new Thread(number);
        Thread thread01 = new Thread(number);
        thread.setName("線程一");
        thread01.setName("線程二");

        thread.start();
        thread01.start();
    }
}

執行結果:

Java多線程的知識點有哪些

六、JDK5.0新增的線程創建方式

新增方式一:實現Callable接口

與使用 Runnable 相比, Callable 功能更強大些

相比 run() 方法,可以有返回值

方法可以拋出異常

支持泛型的返回值需要

借助 FutureTask 類,比如獲取返回結果

  • Future 接口

可以對具體 Runnable、Callable 任務的執行結果進行取消、查詢是否完成、獲取結果等。
FutrueTask 是 Futrue 接口的唯一的實現類
FutureTask 同時實現了 Runnable, Future 接口。它既可以作為 Runnable 被線程執行,又可以作為 Future 得到 Callable 的返回值

  • 新增方式二:使用線程池

沒使用線程池: 經常創建和銷毀、使用量特別大的資源,比如并發情況下的線程,對性能影響很大。
使用線程池后: 提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放回池中。可以避免頻繁創建銷毀、實現重復利用,類似生活中的公共交通工具。

  • 好處:

提高響應速度(減少了創建新線程的時間)
降低資源消耗(重復利用線程池中線程,不需要每次都創建)
便于線程管理
corePoolSize: 核心池的大小
maximumPoolSize: 最大線程數
keepAliveTime: 線程沒有任務時最多保持多長時間后會終止

  • 線程池相關的API

JDK 5.0 起提供了線程池相關 API:ExecutorService 和 Executors
ExecutorService: 真正的線程池接口。常見子類 ThreadPoolExecutor
void execute(Runnable command) : 執行任務命令,沒有返回值,一般用來執行 Runnable
Future submit(Callable task): 執行任務,有返回值,一般又來執行 Callable

  • void shutdown() : 關閉連接池

Executors: 工具類、線程池的工廠類,用于創建并返回不同類型的線程池
Executors.newCachedThreadPool(): 創建一個可根據需要創建新線程的線程池
Executors.newFixedThreadPool(n): 創建一個可重用固定線程數的線程池
Executors.newSingleThreadExecutor() : 創建一個只有一個線程的線程池
Executors.newScheduledThreadPool(n): 創建一個線程池,它可安排在給定延遲后運行命令或者定期地執行。

  • 代碼演示:

創建線程的方式三:使用 Callable 接口

//1.創建一個實現Callable的實現類
class NumThread implements Callable<Integer> {
    //2.實現call方法,將此線程需要執行的操作聲明在call()中
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.創建Callable接口實現類的對象
        NumThread numThread = new NumThread();
        //4.將此Callable接口實現類的對象作為傳遞到FutureTask構造器中,創建FutureTask的對象
        FutureTask<Integer> futureTask = new FutureTask<Integer>(numThread);
        //5.將FutureTask的對象作為參數傳遞到Thread類的構造器中,創建Thread對象,并調用start()
        new Thread(futureTask).start();

        try {
            //6.獲取Callable中call方法的返回值
            //get()返回值即為FutureTask構造器參數Callable實現類重寫的call()的返回值。
            Integer sum = futureTask.get();
            System.out.println("總和為:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

創建線程方式四:使用線程池技術

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. 提供指定線程數量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //設置線程池的屬性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
        service.execute(new NumberThread());//適合適用于Runnable
        service.execute(new NumberThread1());//適合適用于Runnable

//        service.submit(Callable callable);//適合使用于Callable
        //3.關閉連接池
        service.shutdown();
    }

}

讀到這里,這篇“Java多線程的知識點有哪些”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

广州市| 永康市| 泗阳县| 柘荣县| 吴忠市| 台北市| 乌鲁木齐县| 凤阳县| 武城县| 青川县| 长泰县| 福安市| 屯留县| 怀柔区| 南丹县| 泗阳县| 五华县| 北海市| 集贤县| 临江市| 鞍山市| 成安县| 芒康县| 定南县| 准格尔旗| 手游| 康定县| 淮北市| 潮州市| 宜兰县| 准格尔旗| 沂水县| 江都市| 云浮市| 盐津县| 阜新市| 大安市| 深圳市| 东阿县| 高唐县| 清水河县|