最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java多線程、線程安全、線程池創(chuàng)建方式

 更新時(shí)間:2026年03月11日 09:25:38   作者:jaysee-sjc  
本文詳細(xì)介紹了多線程的基本概念、創(chuàng)建方式、常用方法、線程安全問(wèn)題解決以及線程池的使用,通過(guò)具體案例和代碼示例,幫助讀者理解多線程在Java中的應(yīng)用和實(shí)現(xiàn),感興趣的朋友跟隨小編一起看看吧

多線程是實(shí)現(xiàn)多任務(wù)并行處理的核心技術(shù),小到程序的異步執(zhí)行,大到高并發(fā)系統(tǒng)的開(kāi)發(fā),都離不開(kāi)多線程。比如我們平時(shí)用的軟件,一邊聽(tīng)歌一邊刷評(píng)論,就是多線程的典型場(chǎng)景。

一、多線程:

1.1 兩個(gè)基礎(chǔ)概念

簡(jiǎn)單說(shuō):進(jìn)程是軟件的實(shí)例,線程是軟件里的具體任務(wù)

多線程就是讓一個(gè)程序同時(shí)執(zhí)行多個(gè)任務(wù)。

  • 進(jìn)程:運(yùn)行中的程序,比如打開(kāi)的微信、IDEA,是操作系統(tǒng)資源分配的基本單位。
  • 線程:進(jìn)程中的最小執(zhí)行單元,一個(gè)進(jìn)程可以包含多個(gè)線程,線程共享進(jìn)程的資源(內(nèi)存、文件等),是 CPU 調(diào)度的基本單位。

1.2 三種創(chuàng)建方式

Java 中創(chuàng)建多線程有3 種核心方式,各有優(yōu)缺點(diǎn),適用于不同場(chǎng)景,入門(mén)先掌握這三種即可,其中實(shí)現(xiàn) Runnable 接口是最常用的方式。

方式 1:繼承 Thread 類(lèi)

Thread類(lèi)是 Java 多線程的核心類(lèi),直接繼承它,重寫(xiě)run()方法(線程的執(zhí)行體),調(diào)用start()方法啟動(dòng)線程。

核心步驟

  1. 自定義類(lèi)繼承Thread類(lèi);
  2. 重寫(xiě)run()方法,編寫(xiě)線程執(zhí)行邏輯;
  3. 創(chuàng)建自定義線程對(duì)象,調(diào)用start()方法啟動(dòng)(注意:不能直接調(diào)用 run (),否則就是普通方法)。

代碼示例

// 1. 自定義線程類(lèi),繼承Thread
public class MyThread extends Thread {
    // 2. 重寫(xiě)run()方法,線程執(zhí)行的邏輯寫(xiě)在這里
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            // Thread.currentThread().getName() 獲取當(dāng)前線程名
            System.out.println(Thread.currentThread().getName() + ":執(zhí)行了" + i);
        }
    }
    public static void main(String[] args) {
        // 3. 創(chuàng)建線程對(duì)象,設(shè)置線程名
        MyThread t1 = new MyThread();
        t1.setName("線程1");
        MyThread t2 = new MyThread();
        t2.setName("線程2");
        // 4. 啟動(dòng)線程:調(diào)用start()
        t1.start();
        t2.start();
        // 主線程也執(zhí)行邏輯,對(duì)比看多線程效果
        for (int i = 0; i < 5; i++) {
            System.out.println("主線程:執(zhí)行了" + i);
        }
    }
}

運(yùn)行結(jié)果

多線程的執(zhí)行順序由CPU 調(diào)度決定,所以每次運(yùn)行結(jié)果都不一樣,體現(xiàn)了線程的隨機(jī)性

主線程:執(zhí)行了0
線程1:執(zhí)行了0
線程2:執(zhí)行了0
線程1:執(zhí)行了1
主線程:執(zhí)行了1
...

缺點(diǎn)

Java 是單繼承,自定義類(lèi)繼承 Thread 后,就不能繼承其他類(lèi),靈活性差。

方式 2:實(shí)現(xiàn) Runnable 接口

Runnable是線程的任務(wù)接口,只定義了一個(gè)run()方法,把線程和任務(wù)分離,更符合面向?qū)ο笏枷?,是開(kāi)發(fā)中最常用的方式。

核心步驟

  1. 自定義類(lèi)實(shí)現(xiàn)Runnable接口,重寫(xiě)run()方法;
  2. 創(chuàng)建任務(wù)對(duì)象;
  3. 把任務(wù)對(duì)象傳給Thread類(lèi)的構(gòu)造器,創(chuàng)建線程對(duì)象;
  4. 調(diào)用start()啟動(dòng)線程。

代碼示例

// 1. 自定義任務(wù)類(lèi),實(shí)現(xiàn)Runnable接口
public class MyRunnable implements Runnable {
    // 2. 重寫(xiě)run()方法,編寫(xiě)任務(wù)邏輯
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":執(zhí)行了" + i);
        }
    }
    public static void main(String[] args) {
        // 3. 創(chuàng)建任務(wù)對(duì)象
        MyRunnable task = new MyRunnable();
        // 4. 把任務(wù)傳給Thread,創(chuàng)建線程對(duì)象
        Thread t1 = new Thread(task, "線程1");
        Thread t2 = new Thread(task, "線程2");
        // 5. 啟動(dòng)線程
        t1.start();
        t2.start();
    }
}

優(yōu)點(diǎn)

  1. 避免單繼承的限制,自定義類(lèi)可以繼承其他類(lèi);
  2. 線程和任務(wù)分離,一個(gè)任務(wù)可以被多個(gè)線程執(zhí)行,資源復(fù)用。

方式 3:實(shí)現(xiàn) Callable 接口

前兩種方式的run()方法沒(méi)有返回值,不能拋出編譯時(shí)異常,如果需要線程執(zhí)行后返回結(jié)果(比如計(jì)算一個(gè)數(shù)值),就用Callable接口,這是 JDK5 新增的方式。

核心要點(diǎn)

  • Callable<V>:泛型 V 表示返回值類(lèi)型,重寫(xiě)call()方法(執(zhí)行體),有返回值,可拋異常;
  • 需要配合FutureTask類(lèi)使用:封裝Callable任務(wù),獲取線程執(zhí)行結(jié)果;
  • 最終還是通過(guò)Thread類(lèi)啟動(dòng)線程。

代碼示例

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
// 1. 自定義任務(wù)類(lèi),實(shí)現(xiàn)Callable接口,指定泛型為返回值類(lèi)型(Integer)
public class MyCallable implements Callable<Integer> {
    // 2. 重寫(xiě)call()方法,有返回值,可拋異常
    @Override
    public Integer call() throws Exception {
        // 模擬計(jì)算:1-5的和
        int sum = 0;
        for (int i = 1; i <= 5; i++) {
            sum += i;
            System.out.println(Thread.currentThread().getName() + ":計(jì)算中,i=" + i);
        }
        return sum; // 返回計(jì)算結(jié)果
    }
    public static void main(String[] args) throws Exception {
        // 3. 創(chuàng)建Callable任務(wù)對(duì)象
        MyCallable callable = new MyCallable();
        // 4. 用FutureTask封裝任務(wù),用于獲取返回值
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        // 5. 把FutureTask傳給Thread,創(chuàng)建線程并啟動(dòng)
        Thread t = new Thread(futureTask, "計(jì)算線程");
        t.start();
        // 6. 獲取線程執(zhí)行結(jié)果:get()方法會(huì)阻塞,直到線程執(zhí)行完成
        Integer result = futureTask.get();
        System.out.println(t.getName() + "執(zhí)行結(jié)果:1-5的和=" + result);
    }
}

運(yùn)行結(jié)果

計(jì)算線程:計(jì)算中,i=1
計(jì)算線程:計(jì)算中,i=2
計(jì)算線程:計(jì)算中,i=3
計(jì)算線程:計(jì)算中,i=4
計(jì)算線程:計(jì)算中,i=5
計(jì)算線程執(zhí)行結(jié)果:1-5的和=15

優(yōu)點(diǎn)

  1. 有返回值,能滿足 “線程執(zhí)行后需要結(jié)果” 的場(chǎng)景;
  2. call()方法可以拋出編譯時(shí)異常,無(wú)需手動(dòng)捕獲。

1.3 多線程常用方法

整理了開(kāi)發(fā)中最常用的線程方法,分為線程啟動(dòng) / 命名、線程休眠 / 等待線程狀態(tài)獲取三類(lèi):

方法名作用注意事項(xiàng)
start()啟動(dòng)線程,JVM 會(huì)自動(dòng)調(diào)用run()不能重復(fù)調(diào)用,否則拋異常
run()線程執(zhí)行體,編寫(xiě)業(yè)務(wù)邏輯直接調(diào)用是普通方法,不會(huì)啟動(dòng)新線程
setName(String name)設(shè)置線程名也可以通過(guò) Thread 構(gòu)造器設(shè)置
getName()獲取線程名默認(rèn)線程名:Thread-0、Thread-1...
sleep(long ms)讓線程休眠指定毫秒數(shù)靜態(tài)方法,休眠時(shí)不會(huì)釋放鎖,會(huì)拋出 InterruptedException
join()讓調(diào)用該方法的線程先執(zhí)行完比如 t1.join (),主線程會(huì)等 t1 執(zhí)行完再執(zhí)行
Thread.currentThread()獲取當(dāng)前執(zhí)行的線程對(duì)象靜態(tài)方法,常用在匿名線程中
isAlive()判斷線程是否存活啟動(dòng)后、結(jié)束前為 true

常用方法示例:

public class ThreadMethodDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                try {
                    Thread.sleep(1000); // 休眠1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":執(zhí)行" + i);
            }
        }, "休眠線程");
        t.start();
        t.join(); // 主線程等待t執(zhí)行完再執(zhí)行
        System.out.println("主線程:執(zhí)行完成,休眠線程已結(jié)束");
    }
}

運(yùn)行結(jié)果

休眠線程:執(zhí)行0
休眠線程:執(zhí)行1
休眠線程:執(zhí)行2
主線程:執(zhí)行完成,休眠線程已結(jié)束

二、線程安全:

2.1 什么是線程安全問(wèn)題?

當(dāng)多個(gè)線程同時(shí)操作同一個(gè)共享資源時(shí),會(huì)導(dǎo)致數(shù)據(jù)錯(cuò)亂、結(jié)果不符合預(yù)期,這就是線程安全問(wèn)題。

核心條件

  1. 多線程環(huán)境  
  2. 操作共享資源  
  3. 共享資源的操作是非原子性的(一行代碼可能對(duì)應(yīng)多個(gè) CPU 指令)。

2.2 模擬線程安全問(wèn)題:

假設(shè)火車(chē)站有 10 張票,3 個(gè)窗口同時(shí)賣(mài)票,模擬多線程售票,會(huì)出現(xiàn)超賣(mài)(賣(mài)成 0 張以下)、重復(fù)賣(mài)的問(wèn)題,這是典型的線程安全問(wèn)題。

代碼示例

// 售票任務(wù)類(lèi),實(shí)現(xiàn)Runnable
public class TicketRunnable implements Runnable {
    // 共享資源:10張票(多個(gè)線程共用這個(gè)變量)
    private int ticketNum = 10;
    @Override
    public void run() {
        // 循環(huán)賣(mài)票
        while (true) {
            // 有票就賣(mài)
            if (ticketNum > 0) {
                try {
                    Thread.sleep(500); // 模擬選座、打印票的耗時(shí)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "賣(mài)出了1張票,剩余:" + --ticketNum);
            } else {
                // 無(wú)票就退出
                break;
            }
        }
    }
    public static void main(String[] args) {
        // 一個(gè)任務(wù)對(duì)象,3個(gè)線程共享
        TicketRunnable task = new TicketRunnable();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

運(yùn)行結(jié)果

窗口1賣(mài)出了1張票,剩余:9
窗口2賣(mài)出了1張票,剩余:8
窗口3賣(mài)出了1張票,剩余:7
...
窗口2賣(mài)出了1張票,剩余:1
窗口3賣(mài)出了1張票,剩余:0
窗口1賣(mài)出了1張票,剩余:-1 // 超賣(mài),線程安全問(wèn)題

問(wèn)題原因

  • 多個(gè)線程同時(shí)判斷ticketNum > 0,比如票只剩 1 張時(shí),3 個(gè)線程都判斷為 true,然后依次執(zhí)行--ticketNum,最終就會(huì)賣(mài)成 - 1。

2.3 解決線程安全問(wèn)題:線程同步

解決線程安全問(wèn)題的核心思路是線程同步,也就是給共享資源的操作代碼加鎖,讓多個(gè)線程排隊(duì)執(zhí)行這部分代碼,而不是同時(shí)執(zhí)行。

Java 中提供了3 種加鎖方式同步代碼塊同步方法、Lock 鎖,都能解決線程安全問(wèn)題,下面基于售票案例逐一實(shí)現(xiàn)。

方式 1:同步代碼塊(synchronized)

synchronized關(guān)鍵字包裹操作共享資源的代碼塊,指定鎖對(duì)象,多個(gè)線程必須使用同一個(gè)鎖對(duì)象才能實(shí)現(xiàn)同步。

語(yǔ)法

synchronized(鎖對(duì)象) {
    // 操作共享資源的代碼塊(臨界區(qū))
}

鎖對(duì)象要求

  1. 可以是任意對(duì)象,但多個(gè)線程的鎖對(duì)象必須是同一個(gè);
  2. 推薦使用共享資源本身this(當(dāng)前任務(wù)對(duì)象)作為鎖對(duì)象。

售票案例改造

public class TicketSyncBlock implements Runnable {
    private int ticketNum = 10;
    // 鎖對(duì)象:多個(gè)線程共用同一個(gè)
    private Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            // 同步代碼塊:加鎖,操作共享資源的代碼排隊(duì)執(zhí)行
            synchronized (lock) {
                if (ticketNum > 0) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣(mài)出了1張票,剩余:" + --ticketNum);
                } else {
                    break;
                }
            }
        }
    }
    public static void main(String[] args) {
        TicketSyncBlock task = new TicketSyncBlock();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

運(yùn)行結(jié)果

窗口1賣(mài)出了1張票,剩余:9
窗口1賣(mài)出了1張票,剩余:8
窗口2賣(mài)出了1張票,剩余:7
...
窗口3賣(mài)出了1張票,剩余:1
窗口3賣(mài)出了1張票,剩余:0

方式 2:同步方法

把操作共享資源的代碼提取成一個(gè)方法,用synchronized修飾,這個(gè)方法就是同步方法,無(wú)需手動(dòng)指定鎖對(duì)象。

鎖對(duì)象規(guī)則

  1. 非靜態(tài)同步方法:鎖對(duì)象是this(當(dāng)前任務(wù)對(duì)象);
  2. 靜態(tài)同步方法:鎖對(duì)象是類(lèi)的 Class 對(duì)象(比如 TicketSyncMethod.class)。

售票案例改造

public class TicketSyncMethod implements Runnable {
    private int ticketNum = 10;
    @Override
    public void run() {
        while (true) {
            // 調(diào)用同步方法
            if (!sellTicket()) {
                break;
            }
        }
    }
    // 同步方法:synchronized修飾,操作共享資源的代碼
    private synchronized boolean sellTicket() {
        if (ticketNum > 0) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣(mài)出了1張票,剩余:" + --ticketNum);
            return true;
        } else {
            return false;
        }
    }
    public static void main(String[] args) {
        TicketSyncMethod task = new TicketSyncMethod();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

優(yōu)點(diǎn)

代碼更簡(jiǎn)潔,無(wú)需手動(dòng)創(chuàng)建鎖對(duì)象,適合整個(gè)方法都是操作共享資源的場(chǎng)景。

方式 3:Lock 鎖

synchronized是隱式鎖(自動(dòng)加鎖、自動(dòng)釋放鎖,代碼執(zhí)行完 / 拋異常都會(huì)釋放),而Lock是顯式鎖,需要手動(dòng)加鎖和手動(dòng)釋放鎖,靈活性更高,是開(kāi)發(fā)中常用的進(jìn)階方式。

Java 中Lock的核心實(shí)現(xiàn)類(lèi)是ReentrantLock(可重入鎖)。

核心方法

  • lock():手動(dòng)加鎖;
  • unlock():手動(dòng)釋放鎖(必須放在 finally 中,保證鎖一定釋放,避免死鎖)。

售票案例改造

import java.util.concurrent.locks.ReentrantLock;
public class TicketLock implements Runnable {
    private int ticketNum = 10;
    // 創(chuàng)建Lock鎖對(duì)象,可重入鎖
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            // 手動(dòng)加鎖
            lock.lock();
            try {
                // 操作共享資源的代碼
                if (ticketNum > 0) {
                    Thread.sleep(500);
                    System.out.println(Thread.currentThread().getName() + "賣(mài)出了1張票,剩余:" + --ticketNum);
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 手動(dòng)釋放鎖,放在finally中,保證一定執(zhí)行
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        TicketLock task = new TicketLock();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

優(yōu)點(diǎn)

  1. 靈活性高:可以在代碼任意位置加鎖、釋放鎖,不像 synchronized 只能包裹代碼塊 / 修飾方法;
  2. 支持公平鎖 / 非公平鎖:創(chuàng)建 ReentrantLock 時(shí)傳入true就是公平鎖(先等待的線程先執(zhí)行);
  3. 提供更多方法:比如tryLock()(嘗試加鎖,加鎖失敗不阻塞)。

2.4 三種同步方式對(duì)比

同步方式鎖類(lèi)型加鎖 / 釋放靈活性適用場(chǎng)景
同步代碼塊隱式鎖自動(dòng)中等部分代碼操作共享資源
同步方法隱式鎖自動(dòng)整個(gè)方法操作共享資源
Lock 鎖顯式鎖手動(dòng)(finally 釋放)復(fù)雜的同步場(chǎng)景,需要靈活加鎖 / 釋放

核心總結(jié):解決線程安全問(wèn)題的本質(zhì)是讓多個(gè)線程排隊(duì)執(zhí)行共享資源的代碼,加鎖是實(shí)現(xiàn)排隊(duì)的關(guān)鍵。

三、線程池:

3.1 為什么需要線程池?

線程池就是一個(gè)線程的容器,提前創(chuàng)建好若干個(gè)線程,任務(wù)來(lái)了直接分配線程執(zhí)行,任務(wù)執(zhí)行完線程不銷(xiāo)毀,放回線程池復(fù)用。就像餐廳的服務(wù)員,不會(huì)來(lái)了一個(gè)客人就雇一個(gè)服務(wù)員,而是提前雇好,客人走了服務(wù)員繼續(xù)服務(wù)下一個(gè)客人。

手動(dòng)創(chuàng)建線程雖然簡(jiǎn)單,但存在很多問(wèn)題:

  1. 線程創(chuàng)建和銷(xiāo)毀消耗系統(tǒng)資源(CPU、內(nèi)存);
  2. 無(wú)限制創(chuàng)建線程會(huì)導(dǎo)致內(nèi)存溢出(OOM);
  3. 手動(dòng)管理線程的狀態(tài)(啟動(dòng)、停止)非常繁瑣。

3.2 線程池的核心優(yōu)勢(shì)

  1. 線程復(fù)用:避免頻繁創(chuàng)建銷(xiāo)毀線程,減少資源開(kāi)銷(xiāo);
  2. 控制線程數(shù)量:防止創(chuàng)建過(guò)多線程導(dǎo)致內(nèi)存溢出;
  3. 提高響應(yīng)速度:任務(wù)來(lái)了直接用池中的線程,無(wú)需等待線程創(chuàng)建;
  4. 統(tǒng)一管理:方便對(duì)線程進(jìn)行監(jiān)控、統(tǒng)計(jì)、調(diào)優(yōu)。

3.3 線程池處理兩種任務(wù):

線程池可以處理我們之前講的Runnable 任務(wù)(無(wú)返回值)Callable 任務(wù)(有返回值),核心步驟都是:

  1. 創(chuàng)建線程池;
  2. 提交任務(wù)給線程池;
  3. 關(guān)閉線程池(可選,根據(jù)業(yè)務(wù)場(chǎng)景)。

Java 中線程池的核心接口是ExecutorService,下面用最常用的固定線程池演示處理兩種任務(wù)。

案例 1:線程池處理 Runnable 任務(wù)(無(wú)返回值)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolRunnable {
    public static void main(String[] args) {
        // 1. 創(chuàng)建固定線程池:核心線程數(shù)為2
        ExecutorService pool = Executors.newFixedThreadPool(2);
        //使用線程池的實(shí)現(xiàn)類(lèi)ThreadPoolExecutor創(chuàng)建線程池對(duì)象。(有七個(gè)參數(shù))
        //ExecutorService pool = new ThreadPoolExecutor(
        //        2,//核心線程數(shù)
        //        2,//最大線程數(shù)
        //        0,//線程空閑時(shí)間
        //        TimeUnit.SECONDS,//時(shí)間單位
        //        new ArrayBlockingQueue<>(2),//任務(wù)隊(duì)列
        //        Executors.defaultThreadFactory(),//線程工廠對(duì)象
        //        new ThreadPoolExecutor.AbortPolicy()//拒絕策略
        //);
        // 2. 提交Runnable任務(wù)給線程池
        pool.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println(Thread.currentThread().getName() + "執(zhí)行Runnable任務(wù):" + i);
                }
            }
        });
        pool.submit(() -> { // Lambda簡(jiǎn)化Runnable
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "執(zhí)行Lambda任務(wù):" + i);
            }
        });
        // 3. 關(guān)閉線程池:shutdown()等待任務(wù)執(zhí)行完再關(guān)閉,shutdownNow()立即關(guān)閉
        pool.shutdown();
    }
}

案例 2:線程池處理 Callable 任務(wù)(有返回值)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadPoolCallable {
    public static void main(String[] args) throws Exception {
        // 1. 創(chuàng)建固定線程池,核心線程數(shù)1
        ExecutorService pool = Executors.newFixedThreadPool(1);
        // 2. 提交Callable任務(wù),F(xiàn)uture接收返回值
        Future<Integer> future = pool.submit(() -> {
            // 計(jì)算1-10的和
            int sum = 0;
            for (int i = 1; i <= 10; i++) {
                sum += i;
            }
            return sum;
        });
        // 3. 獲取返回值
        Integer result = future.get();
        System.out.println("Callable任務(wù)執(zhí)行結(jié)果:1-10的和=" + result);
        // 4. 關(guān)閉線程池
        pool.shutdown();
    }
}

運(yùn)行結(jié)果

Callable任務(wù)執(zhí)行結(jié)果:1-10的和=55

3.4 Executors 工具類(lèi):

Java 提供了java.util.concurrent.Executors工具類(lèi),封裝了線程池的創(chuàng)建邏輯,提供了4 種常用的線程池創(chuàng)建方法,滿足 90% 的開(kāi)發(fā)需求,入門(mén)直接用這個(gè)工具類(lèi)即可。

4 種常用線程池總結(jié)

線程池方法核心特點(diǎn)核心線程數(shù)最大線程數(shù)適用場(chǎng)景
newFixedThreadPool(n)固定線程池,線程數(shù)固定,任務(wù)排隊(duì)nn任務(wù)量固定,需要穩(wěn)定執(zhí)行的場(chǎng)景(比如常規(guī)業(yè)務(wù)處理)
newSingleThreadExecutor()單線程池,只有 1 個(gè)線程11任務(wù)需要按順序執(zhí)行的場(chǎng)景
newCachedThreadPool()緩存線程池,線程數(shù)動(dòng)態(tài)擴(kuò)容,空閑線程 60 秒銷(xiāo)毀0Integer.MAX_VALUE任務(wù)量波動(dòng)大的場(chǎng)景(比如高并發(fā)請(qǐng)求)
newScheduledThreadPool(n)定時(shí)線程池,支持定時(shí) / 延遲執(zhí)行任務(wù)nInteger.MAX_VALUE定時(shí)任務(wù)、延遲任務(wù)(比如定時(shí)備份、定時(shí)統(tǒng)計(jì))

定時(shí)線程池示例

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        // 創(chuàng)建定時(shí)線程池,核心線程數(shù)2
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
        // 1. 延遲執(zhí)行:延遲1秒后執(zhí)行一次任務(wù)
        pool.schedule(() -> {
            System.out.println(Thread.currentThread().getName() + ":延遲1秒執(zhí)行");
        }, 1, TimeUnit.SECONDS);
        // 2. 定時(shí)執(zhí)行:延遲2秒后,每3秒執(zhí)行一次任務(wù)
        pool.scheduleAtFixedRate(() -> {
            System.out.println(Thread.currentThread().getName() + ":定時(shí)執(zhí)行,每3秒一次");
        }, 2, 3, TimeUnit.SECONDS);
    }
}

運(yùn)行結(jié)果

pool-1-thread-1:延遲1秒執(zhí)行
pool-1-thread-2:定時(shí)執(zhí)行,每3秒一次
pool-1-thread-2:定時(shí)執(zhí)行,每3秒一次
...

3.5 并發(fā) & 并行

學(xué)習(xí)多線程,一定會(huì)遇到并發(fā)并行,兩者都表示 “同時(shí)執(zhí)行”,但本質(zhì)不同,用通俗的語(yǔ)言和例子講清楚:

并發(fā)(Concurrency)

  • 定義同一時(shí)間段內(nèi)多個(gè)任務(wù)交替執(zhí)行,看似同時(shí)執(zhí)行,實(shí)際是 CPU 在多個(gè)任務(wù)之間快速切換;
  • 底層:適用于單核 CPU
  • 例子:一個(gè)廚師同時(shí)做兩碗面,先煮第一碗的面,再煮第二碗的面,交替進(jìn)行。

并行(Parallelism)

  • 定義同一時(shí)刻多個(gè)任務(wù)同時(shí)執(zhí)行,真正的同時(shí)執(zhí)行;
  • 底層:適用于多核 CPU,每個(gè)核執(zhí)行一個(gè)任務(wù);
  • 例子:兩個(gè)廚師同時(shí)做兩碗面,各自做各自的,互不干擾。

并發(fā)是交替執(zhí)行,并行是同時(shí)執(zhí)行;Java 多線程在單核 CPU 上是并發(fā),在多核 CPU 上是并行。

四、核心知識(shí)點(diǎn)總結(jié)

  1. 多線程創(chuàng)建:繼承 Thread(單繼承限制)、實(shí)現(xiàn) Runnable(最常用,無(wú)繼承限制)、實(shí)現(xiàn) Callable(帶返回值),啟動(dòng)線程必須調(diào)用start();
  2. 線程安全:多線程操作共享資源導(dǎo)致數(shù)據(jù)錯(cuò)亂,解決方式是加鎖(同步代碼塊、同步方法、Lock 鎖),核心是讓線程排隊(duì)執(zhí)行;
  3. 線程池:Executors 工具類(lèi)快速創(chuàng)建 4 種常用線程池,處理 Runnable(無(wú)返回值)和 Callable(有返回值)任務(wù),優(yōu)勢(shì)是線程復(fù)用、控制數(shù)量;
  4. 并發(fā) & 并行:并發(fā)是單核交替執(zhí)行,并行是多核同時(shí)執(zhí)行。

到此這篇關(guān)于Java多線程、線程安全、線程池創(chuàng)建方式的文章就介紹到這了,更多相關(guān)Java多線程 線程安全 線程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談JVM垃圾回收之哪些對(duì)象可以被回收

    淺談JVM垃圾回收之哪些對(duì)象可以被回收

    這篇文章主要介紹了JVM垃圾回收之哪些對(duì)象可以被回收,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • SpringMVC的執(zhí)行過(guò)程淺析

    SpringMVC的執(zhí)行過(guò)程淺析

    這篇文章主要給大家介紹了關(guān)于SpringMVC的執(zhí)行過(guò)程的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用SpringMVC具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 詳解idea maven項(xiàng)目如何使用lib下得jar包

    詳解idea maven項(xiàng)目如何使用lib下得jar包

    這篇文章主要介紹了詳解idea maven項(xiàng)目如何使用lib下得jar包,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • springBoot中動(dòng)態(tài)加載jar包的完整指南

    springBoot中動(dòng)態(tài)加載jar包的完整指南

    這篇文章主要為大家詳細(xì)介紹了springBoot中動(dòng)態(tài)加載jar包的相關(guān)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-10-10
  • MyBatis-Plus的基本CRUD操作大全

    MyBatis-Plus的基本CRUD操作大全

    本文介紹了MyBatis-Plus框架中BaseMapper和IService兩大核心組件的使用,,通過(guò)示例代碼演示了如何實(shí)現(xiàn)數(shù)據(jù)插入、刪除、修改、查詢等操作,并展示了條件構(gòu)造器、分頁(yè)查詢、事務(wù)處理等實(shí)用功能,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)五子棋游戲

    java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)五子棋游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單控制臺(tái)五子棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • 實(shí)例講解java的純數(shù)字加密解密

    實(shí)例講解java的純數(shù)字加密解密

    本文給大家分享的是一個(gè)java純數(shù)字加密解密技術(shù),加密和解密本身就是一對(duì)共生體,缺一不可,需要的朋友可以參考下
    2015-07-07
  • Java中的權(quán)限修飾符(protected)示例詳解

    Java中的權(quán)限修飾符(protected)示例詳解

    這篇文章主要給大家介紹了關(guān)于Java中權(quán)限修飾符(protected)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 詳解如何使用IntelliJ IDEA生成UML圖

    詳解如何使用IntelliJ IDEA生成UML圖

    在軟件開(kāi)發(fā)中,UML(統(tǒng)一建模語(yǔ)言)是一種用于描述、構(gòu)建和文檔化軟件系統(tǒng)的圖形化語(yǔ)言,它幫助開(kāi)發(fā)者以可視化的方式理解系統(tǒng)的結(jié)構(gòu)和行為,手動(dòng)繪制 UML 圖可能既耗時(shí)又容易出錯(cuò),所以本文給大家介紹了如何使用IntelliJ IDEA生成UML圖,需要的朋友可以參考下
    2024-10-10
  • 關(guān)于SSM框架下各層的解釋說(shuō)明(Controller等)

    關(guān)于SSM框架下各層的解釋說(shuō)明(Controller等)

    這篇文章主要介紹了關(guān)于SSM框架下各層的解釋說(shuō)明(Controller等),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論

太白县| 昆山市| 新野县| 四子王旗| 册亨县| 湾仔区| 故城县| 河间市| 闻喜县| 柳林县| 囊谦县| 哈尔滨市| 高尔夫| 小金县| 岑溪市| 长岭县| 巴里| 兴化市| 威信县| 唐山市| 四子王旗| 徐州市| 昌都县| 芜湖县| 洪洞县| 武穴市| 汝阳县| 新巴尔虎左旗| 清原| 大化| 临夏市| 金川县| 德兴市| 湖南省| 军事| 荣昌县| 汝城县| 邢台县| 安新县| 杂多县| 乌苏市|