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

Java多線程編程怎么學(xué)?線程同步與線程池實戰(zhàn)詳解

 更新時間:2026年07月11日 11:51:59   作者:小林說AI  
還在為Java多線程的進程線程區(qū)別、線程創(chuàng)建方式、同步鎖和線程池頭疼嗎?本文用通俗易懂的講解和代碼實操,帶你徹底掌握線程同步機制與線程池實戰(zhàn),解決并發(fā)安全問題,提升程序性能,輕松應(yīng)對面試和開發(fā)

1.1 本章學(xué)習(xí)目標與重點

?? 掌握線程的核心概念,理解進程與線程的區(qū)別和聯(lián)系。
?? 熟練掌握線程的三種創(chuàng)建方式,理解線程的生命周期及狀態(tài)轉(zhuǎn)換。
?? 掌握線程同步與鎖機制,解決多線程并發(fā)安全問題。
?? 了解線程池的核心原理與使用方法,提升多線程程序性能。
?? 本章重點是 線程同步機制線程池的實戰(zhàn)應(yīng)用,這是多線程開發(fā)中的核心難點和高頻考點。

1.2 多線程核心概念

1.2.1 進程與線程的區(qū)別

?? 進程是操作系統(tǒng)進行資源分配和調(diào)度的基本單位,每個進程都有獨立的內(nèi)存空間和系統(tǒng)資源。比如打開一個 Java 程序,就會啟動一個進程。
?? 線程是進程的執(zhí)行單元,是 CPU 調(diào)度和執(zhí)行的基本單位。一個進程可以包含多個線程,這些線程共享進程的內(nèi)存空間和資源。

對比維度進程線程
資源分配擁有獨立的內(nèi)存空間和資源共享所屬進程的內(nèi)存和資源
開銷成本創(chuàng)建和銷毀開銷大創(chuàng)建和銷毀開銷小
調(diào)度方式由操作系統(tǒng)內(nèi)核調(diào)度由進程內(nèi)部調(diào)度
獨立性進程之間相互獨立線程之間共享資源,依賴性強

? 核心結(jié)論:線程是輕量級的進程,多線程編程可以充分利用 CPU 資源,提升程序的并發(fā)執(zhí)行效率。

1.2.2 為什么需要多線程

在單線程程序中,代碼是串行執(zhí)行的,當遇到耗時操作(如文件讀寫、網(wǎng)絡(luò)請求)時,程序會阻塞等待,造成 CPU 資源浪費。
多線程的優(yōu)勢體現(xiàn)在以下場景:

  1. 后臺任務(wù)處理:如電商系統(tǒng)的訂單超時取消、數(shù)據(jù)同步等。
  2. 異步操作:如用戶注冊后發(fā)送短信驗證碼,不阻塞主流程。
  3. 提高 CPU 利用率:如計算密集型任務(wù),多線程可以充分利用多核 CPU。

1.3 線程的創(chuàng)建方式

1.3.1 方式1:繼承 Thread 類

實現(xiàn)步驟

① ?? 定義一個類繼承 Thread 類。
② ?? 重寫 run() 方法,編寫線程執(zhí)行的業(yè)務(wù)邏輯。
③ ?? 創(chuàng)建線程對象,調(diào)用 start() 方法啟動線程。

代碼實操

/**
 * 繼承Thread類創(chuàng)建線程
 */
public class MyThread extends Thread {
    // 線程名稱
    private String threadName;

    public MyThread(String threadName) {
        this.threadName = threadName;
    }

    // 重寫run方法,定義線程執(zhí)行邏輯
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(threadName + " 執(zhí)行:" + i);
            // 模擬線程執(zhí)行耗時操作
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 創(chuàng)建線程對象
        MyThread thread1 = new MyThread("線程A");
        MyThread thread2 = new MyThread("線程B");

        // 啟動線程:注意是調(diào)用start()方法,不是run()方法
        thread1.start();
        thread2.start();
    }
}

輸出結(jié)果(順序不固定,體現(xiàn)線程并發(fā)執(zhí)行)

線程A 執(zhí)行:1
線程B 執(zhí)行:1
線程A 執(zhí)行:2
線程B 執(zhí)行:2
線程A 執(zhí)行:3
線程B 執(zhí)行:3
線程A 執(zhí)行:4
線程B 執(zhí)行:4
線程A 執(zhí)行:5
線程B 執(zhí)行:5

?? 注意事項:啟動線程必須調(diào)用 start() 方法,直接調(diào)用 run() 方法會變成普通方法的串行執(zhí)行。

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

實現(xiàn)步驟

① ?? 定義一個類實現(xiàn) Runnable 接口。
② ?? 重寫 run() 方法,編寫線程執(zhí)行邏輯。
③ ?? 創(chuàng)建 Runnable 實現(xiàn)類對象,作為 Thread 構(gòu)造方法的參數(shù)。
④ ?? 調(diào)用 start() 方法啟動線程。

代碼實操

/**
 * 實現(xiàn)Runnable接口創(chuàng)建線程
 */
public class MyRunnable implements Runnable {
    private String taskName;

    public MyRunnable(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(taskName + " 執(zhí)行:" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 創(chuàng)建任務(wù)對象
        MyRunnable task1 = new MyRunnable("任務(wù)A");
        MyRunnable task2 = new MyRunnable("任務(wù)B");

        // 創(chuàng)建線程對象,傳入任務(wù)
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);

        // 啟動線程
        thread1.start();
        thread2.start();
    }
}

方式2的優(yōu)勢

  1. 避免單繼承的局限性:Java 是單繼承機制,繼承 Thread 類后無法再繼承其他類,而實現(xiàn) Runnable 接口可以。
  2. 資源共享:多個線程可以共享同一個 Runnable 任務(wù)對象,適合多線程處理同一份資源的場景。

資源共享案例:多線程售票

/**
 * 多線程共享資源:模擬售票系統(tǒng)
 */
public class TicketRunnable implements Runnable {
    // 共享資源:10張票
    private int ticketNum = 10;

    @Override
    public void run() {
        while (ticketNum > 0) {
            // 模擬售票耗時
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum-- + " 張票");
        }
    }

    public static void main(String[] args) {
        // 一個任務(wù)對象,多個線程共享
        TicketRunnable ticketTask = new TicketRunnable();

        // 三個線程模擬三個售票窗口
        Thread window1 = new Thread(ticketTask, "窗口1");
        Thread window2 = new Thread(ticketTask, "窗口2");
        Thread window3 = new Thread(ticketTask, "窗口3");

        window1.start();
        window2.start();
        window3.start();
    }
}

輸出結(jié)果(存在線程安全問題,后面會解決)

窗口1 售出第 10 張票
窗口2 售出第 9 張票
窗口3 售出第 8 張票
窗口1 售出第 7 張票
窗口2 售出第 6 張票
窗口3 售出第 5 張票
窗口1 售出第 4 張票
窗口2 售出第 3 張票
窗口3 售出第 2 張票
窗口1 售出第 1 張票

1.3.3 方式3:實現(xiàn) Callable 接口(帶返回值)

實現(xiàn)步驟

① ?? 定義一個類實現(xiàn) Callable<V> 接口,V 是返回值類型。
② ?? 重寫 call() 方法,編寫線程執(zhí)行邏輯,該方法可以拋出異常。
③ ?? 創(chuàng)建 Callable 實現(xiàn)類對象,通過 FutureTask 包裝。
④ ?? 將 FutureTask 作為 Thread 構(gòu)造方法參數(shù),啟動線程。
⑤ ?? 調(diào)用 FutureTaskget() 方法獲取返回值。

代碼實操

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 實現(xiàn)Callable接口創(chuàng)建線程(帶返回值)
 */
public class MyCallable implements Callable<Integer> {
    // 計算1到n的累加和
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
            System.out.println("計算中:" + i + ",當前和為:" + sum);
            Thread.sleep(200);
        }
        return sum;
    }

    public static void main(String[] args) throws Exception {
        // 創(chuàng)建Callable任務(wù)
        MyCallable callableTask = new MyCallable(10);

        // 用FutureTask包裝,用于獲取返回值
        FutureTask<Integer> futureTask = new FutureTask<>(callableTask);

        // 創(chuàng)建線程并啟動
        Thread thread = new Thread(futureTask);
        thread.start();

        // 獲取返回值:get()方法會阻塞,直到線程執(zhí)行完成
        Integer result = futureTask.get();
        System.out.println("1到10的累加和為:" + result);
    }
}

輸出結(jié)果

計算中:1,當前和為:1
計算中:2,當前和為:3
計算中:3,當前和為:6
計算中:4,當前和為:10
計算中:5,當前和為:15
計算中:6,當前和為:21
計算中:7,當前和為:28
計算中:8,當前和為:36
計算中:9,當前和為:45
計算中:10,當前和為:55
1到10的累加和為:55

? 核心結(jié)論:三種創(chuàng)建方式對比

創(chuàng)建方式優(yōu)點缺點
繼承 Thread編程簡單,直接使用 this 獲取線程單繼承局限,無法共享資源
實現(xiàn) Runnable避免單繼承,支持資源共享無返回值,無法拋出受檢異常
實現(xiàn) Callable有返回值,支持異常拋出編程相對復(fù)雜

1.4 線程的生命周期與狀態(tài)轉(zhuǎn)換

1.4.1 線程的六種狀態(tài)

Java 中線程的生命周期包含六種狀態(tài),定義在 Thread.State 枚舉中:

  1. NEW(新建狀態(tài)):線程對象已創(chuàng)建,但未調(diào)用 start() 方法。
  2. RUNNABLE(可運行狀態(tài)):調(diào)用 start() 方法后,線程處于就緒或運行中。
    • 就緒狀態(tài):線程等待 CPU 調(diào)度。
    • 運行狀態(tài):線程獲取 CPU 資源,執(zhí)行 run() 方法。
  3. BLOCKED(阻塞狀態(tài)):線程等待獲取鎖資源時進入該狀態(tài)。
  4. WAITING(等待狀態(tài)):線程調(diào)用 wait()、join() 等方法后進入,需要被其他線程喚醒。
  5. TIMED_WAITING(超時等待狀態(tài)):線程調(diào)用 sleep(long)、wait(long) 等方法后進入,超時自動喚醒。
  6. TERMINATED(終止狀態(tài)):線程執(zhí)行完畢或異常終止。

1.4.2 線程狀態(tài)轉(zhuǎn)換圖(核心流程)

NEW → RUNNABLE:調(diào)用 start() 方法
RUNNABLE → BLOCKED:競爭鎖失敗
BLOCKED → RUNNABLE:獲取到鎖資源
RUNNABLE → WAITING:調(diào)用 wait()/join() 方法
WAITING → RUNNABLE:其他線程調(diào)用 notify()/notifyAll() 方法
RUNNABLE → TIMED_WAITING:調(diào)用 sleep(long)/wait(long) 方法
TIMED_WAITING → RUNNABLE:超時自動喚醒或被其他線程喚醒
RUNNABLE → TERMINATED:run() 方法執(zhí)行完畢或異常終止

1.4.3 常用線程控制方法

方法作用注意事項
start()啟動線程一個線程只能調(diào)用一次
sleep(long millis)讓線程休眠指定時間不會釋放持有的鎖資源
wait()讓線程進入等待狀態(tài)必須在同步代碼塊中調(diào)用,會釋放鎖
notify()喚醒一個等待的線程必須在同步代碼塊中調(diào)用
notifyAll()喚醒所有等待的線程必須在同步代碼塊中調(diào)用
join()等待該線程執(zhí)行完畢可以實現(xiàn)線程的順序執(zhí)行

代碼實操:線程的順序執(zhí)行(join 方法)

/**
 * 使用join方法實現(xiàn)線程順序執(zhí)行
 */
public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("線程A執(zhí)行:" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread threadB = new Thread(() -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("線程B執(zhí)行:" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 啟動線程A
        threadA.start();
        // 等待線程A執(zhí)行完畢,再啟動線程B
        threadA.join();
        threadB.start();
    }
}

輸出結(jié)果(線程A執(zhí)行完才會執(zhí)行線程B)

線程A執(zhí)行:1
線程A執(zhí)行:2
線程A執(zhí)行:3
線程B執(zhí)行:1
線程B執(zhí)行:2
線程B執(zhí)行:3

1.5 線程同步與并發(fā)安全

1.5.1 并發(fā)安全問題的產(chǎn)生

當多個線程同時操作共享資源時,會導(dǎo)致數(shù)據(jù)不一致的問題。比如前面的售票案例,在極端情況下會出現(xiàn)超賣或重復(fù)售票的問題。

問題復(fù)現(xiàn):多線程售票的并發(fā)安全問題

public class UnsafeTicketDemo implements Runnable {
    private int ticketNum = 10;

    @Override
    public void run() {
        while (ticketNum > 0) {
            // 模擬網(wǎng)絡(luò)延遲,放大并發(fā)問題
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 核心問題:判斷和操作不是原子性的
            System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum-- + " 張票");
        }
    }

    public static void main(String[] args) {
        UnsafeTicketDemo task = new UnsafeTicketDemo();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

可能出現(xiàn)的錯誤結(jié)果

窗口1 售出第 10 張票
窗口2 售出第 10 張票
窗口3 售出第 9 張票
窗口1 售出第 8 張票
窗口2 售出第 7 張票
窗口3 售出第 6 張票
窗口1 售出第 5 張票
窗口2 售出第 4 張票
窗口3 售出第 3 張票
窗口1 售出第 2 張票
窗口2 售出第 1 張票
窗口3 售出第 0 張票

可以看到出現(xiàn)了重復(fù)售票售出0號票的問題,這就是典型的并發(fā)安全問題。

1.5.2 解決方案1:同步代碼塊(synchronized)

?? 同步代碼塊的核心是鎖機制,通過 synchronized 關(guān)鍵字鎖定一個對象,保證同一時間只有一個線程能執(zhí)行代碼塊中的內(nèi)容。

語法格式

synchronized (鎖對象) {
    // 需要同步的代碼(操作共享資源的代碼)
}

代碼實操:同步代碼塊解決售票問題

public class SafeTicketDemo1 implements Runnable {
    private int ticketNum = 10;
    // 定義鎖對象:必須是多個線程共享的對象
    private final Object lock = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (ticketNum > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum-- + " 張票");
                } else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        SafeTicketDemo1 task = new SafeTicketDemo1();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

輸出結(jié)果(無重復(fù)、無超賣,解決并發(fā)安全問題)

窗口1 售出第 10 張票
窗口1 售出第 9 張票
窗口2 售出第 8 張票
窗口2 售出第 7 張票
窗口3 售出第 6 張票
窗口3 售出第 5 張票
窗口1 售出第 4 張票
窗口1 售出第 3 張票
窗口2 售出第 2 張票
窗口2 售出第 1 張票

?? 注意事項:鎖對象必須是多個線程共享的對象,否則無法實現(xiàn)同步效果。

1.5.3 解決方案2:同步方法(synchronized)

?? 同步方法是將 synchronized 關(guān)鍵字修飾在方法上,等價于鎖定當前對象(this)。

語法格式

public synchronized 返回值類型 方法名(參數(shù)列表) {
    // 需要同步的代碼
}

代碼實操:同步方法解決售票問題

public class SafeTicketDemo2 implements Runnable {
    private int ticketNum = 10;

    @Override
    public void run() {
        while (true) {
            if (!sellTicket()) {
                break;
            }
        }
    }

    // 同步方法:鎖定的是this對象
    private synchronized boolean sellTicket() {
        if (ticketNum > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum-- + " 張票");
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        SafeTicketDemo2 task = new SafeTicketDemo2();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

1.5.4 解決方案3:Lock 鎖(JUC 包)

?? java.util.concurrent.locks.Lock 是 JDK 5 新增的鎖機制,相比 synchronized 更加靈活,可以手動控制鎖的獲取和釋放。
常用實現(xiàn)類是 ReentrantLock(可重入鎖)。

實現(xiàn)步驟

① ?? 創(chuàng)建 ReentrantLock 對象。
② ?? 在需要同步的代碼前調(diào)用 lock() 方法獲取鎖。
③ ?? 在 finally 塊中調(diào)用 unlock() 方法釋放鎖,確保鎖一定會被釋放。

代碼實操:Lock 鎖解決售票問題

import java.util.concurrent.locks.ReentrantLock;

public class SafeTicketDemo3 implements Runnable {
    private int ticketNum = 10;
    // 創(chuàng)建可重入鎖對象
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            // 獲取鎖
            lock.lock();
            try {
                if (ticketNum > 0) {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + " 售出第 " + ticketNum-- + " 張票");
                } else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 釋放鎖:必須放在finally塊中
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        SafeTicketDemo3 task = new SafeTicketDemo3();
        new Thread(task, "窗口1").start();
        new Thread(task, "窗口2").start();
        new Thread(task, "窗口3").start();
    }
}

? 核心結(jié)論:synchronizedLock 的對比

特性synchronizedLock
鎖獲取釋放自動獲取和釋放手動調(diào)用 lock()/unlock()
靈活性高(支持公平鎖/非公平鎖)
可中斷性不支持支持
條件變量不支持支持(Condition)

1.6 線程池:高效管理多線程

1.6.1 為什么需要線程池

頻繁創(chuàng)建和銷毀線程會消耗大量系統(tǒng)資源,線程池可以復(fù)用線程,降低系統(tǒng)開銷。線程池的核心優(yōu)勢:

  1. 降低資源消耗:復(fù)用已創(chuàng)建的線程,減少創(chuàng)建和銷毀線程的開銷。
  2. 提高響應(yīng)速度:任務(wù)到達時,無需等待線程創(chuàng)建即可立即執(zhí)行。
  3. 便于線程管理:可以控制最大并發(fā)數(shù),避免線程過多導(dǎo)致資源耗盡。

1.6.2 線程池的核心參數(shù)(ThreadPoolExecutor)

Java 中線程池的核心實現(xiàn)類是 ThreadPoolExecutor,其構(gòu)造方法包含 7 個核心參數(shù):

public ThreadPoolExecutor(
    int corePoolSize,                  // 核心線程數(shù)
    int maximumPoolSize,               // 最大線程數(shù)
    long keepAliveTime,                // 非核心線程空閑存活時間
    TimeUnit unit,                     // 時間單位
    BlockingQueue<Runnable> workQueue, // 任務(wù)隊列
    ThreadFactory threadFactory,       // 線程工廠
    RejectedExecutionHandler handler   // 拒絕策略
)

參數(shù)說明

  1. 核心線程數(shù):線程池長期保持的線程數(shù)量,即使空閑也不會銷毀。
  2. 最大線程數(shù):線程池允許創(chuàng)建的最大線程數(shù)。
  3. 空閑存活時間:非核心線程空閑超過該時間會被銷毀。
  4. 任務(wù)隊列:存放等待執(zhí)行的任務(wù),當核心線程都在忙時,任務(wù)會進入隊列。
  5. 拒絕策略:當任務(wù)隊列滿且線程數(shù)達到最大時,如何處理新任務(wù)。

1.6.3 線程池的使用方式

方式1:通過 Executors 工具類創(chuàng)建(快速使用)

Executors 提供了幾種常用的線程池創(chuàng)建方法:

  • newFixedThreadPool(int nThreads):固定大小的線程池。
  • newCachedThreadPool():緩存線程池,線程數(shù)可動態(tài)調(diào)整。
  • newSingleThreadExecutor():單線程池,保證任務(wù)順序執(zhí)行。

代碼實操:固定大小線程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 固定大小線程池的使用
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 創(chuàng)建固定大小的線程池,核心線程數(shù)=最大線程數(shù)=3
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 2. 提交10個任務(wù)
        for (int i = 1; i <= 10; i++) {
            int taskId = i;
            executorService.submit(() -> {
                System.out.println("任務(wù)" + taskId + " 由線程 " + Thread.currentThread().getName() + " 執(zhí)行");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        // 3. 關(guān)閉線程池
        executorService.shutdown();
    }
}

輸出結(jié)果(3個線程復(fù)用執(zhí)行10個任務(wù))

任務(wù)1 由線程 pool-1-thread-1 執(zhí)行
任務(wù)2 由線程 pool-1-thread-2 執(zhí)行
任務(wù)3 由線程 pool-1-thread-3 執(zhí)行
任務(wù)4 由線程 pool-1-thread-1 執(zhí)行
任務(wù)5 由線程 pool-1-thread-2 執(zhí)行
任務(wù)6 由線程 pool-1-thread-3 執(zhí)行
任務(wù)7 由線程 pool-1-thread-1 執(zhí)行
任務(wù)8 由線程 pool-1-thread-2 執(zhí)行
任務(wù)9 由線程 pool-1-thread-3 執(zhí)行
任務(wù)10 由線程 pool-1-thread-1 執(zhí)行

方式2:手動創(chuàng)建 ThreadPoolExecutor(推薦,更可控)

import java.util.concurrent.*;

/**
 * 手動創(chuàng)建線程池(推薦方式)
 */
public class CustomThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 定義線程池參數(shù)
        int corePoolSize = 2;
        int maximumPoolSize = 5;
        long keepAliveTime = 60;
        TimeUnit unit = TimeUnit.SECONDS;
        // 任務(wù)隊列:容量為3的阻塞隊列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(3);
        // 線程工廠:默認線程工廠
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        // 拒絕策略:丟棄任務(wù)并拋出異常
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        // 2. 創(chuàng)建線程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );

        // 3. 提交任務(wù)
        for (int i = 1; i <= 10; i++) {
            int taskId = i;
            try {
                executor.submit(() -> {
                    System.out.println("任務(wù)" + taskId + " 由線程 " + Thread.currentThread().getName() + " 執(zhí)行");
                    Thread.sleep(1000);
                });
            } catch (Exception e) {
                System.out.println("任務(wù)" + taskId + " 被拒絕:" + e.getMessage());
            }
        }

        // 4. 關(guān)閉線程池
        executor.shutdown();
    }
}

輸出結(jié)果(當任務(wù)數(shù)超過 最大線程數(shù)+隊列容量=8 時,新任務(wù)被拒絕)

任務(wù)1 由線程 pool-1-thread-1 執(zhí)行
任務(wù)2 由線程 pool-1-thread-2 執(zhí)行
任務(wù)3 由線程 pool-1-thread-3 執(zhí)行
任務(wù)4 由線程 pool-1-thread-4 執(zhí)行
任務(wù)5 由線程 pool-1-thread-5 執(zhí)行
任務(wù)6 由線程 pool-1-thread-1 執(zhí)行
任務(wù)7 由線程 pool-1-thread-2 執(zhí)行
任務(wù)8 由線程 pool-1-thread-3 執(zhí)行
任務(wù)9 被拒絕:Task java.util.concurrent.FutureTask@6d311334 rejected from java.util.concurrent.ThreadPoolExecutor@682a0b20[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]
任務(wù)10 被拒絕:Task java.util.concurrent.FutureTask@3d075dc0 rejected from java.util.concurrent.ThreadPoolExecutor@682a0b20[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]

?? 注意事項:實際開發(fā)中推薦手動創(chuàng)建線程池,避免使用 Executors 工具類的默認方法,防止資源耗盡。

1.7 實戰(zhàn)案例:多線程實現(xiàn)文件批量下載

1.7.1 需求分析

?? 實現(xiàn)一個多線程文件下載工具,支持從指定 URL 列表批量下載文件,要求:

  1. 使用線程池管理下載線程,控制并發(fā)數(shù)。
  2. 記錄每個文件的下載狀態(tài)(成功/失?。?。
  3. 支持斷點續(xù)傳(可選)。

1.7.2 代碼實現(xiàn)

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 多線程文件批量下載工具
 */
public class MultiThreadFileDownloader {
    // 線程池:核心線程數(shù)2,最大線程數(shù)4
    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(
            2,
            4,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );

    // 下載狀態(tài)列表
    private static final List<String> DOWNLOAD_STATUS = new ArrayList<>();

    /**
     * 下載單個文件
     * @param fileUrl 文件URL
     * @param savePath 保存路徑
     */
    private static void downloadFile(String fileUrl, String savePath) {
        HttpURLConnection connection = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            URL url = new URL(fileUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);

            // 獲取文件名
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            File saveFile = new File(savePath + File.separator + fileName);

            // 讀取文件流并寫入本地
            in = connection.getInputStream();
            out = new FileOutputStream(saveFile);
            byte[] buffer = new byte[1024 * 8];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }

            String successMsg = "成功:" + fileName;
            synchronized (DOWNLOAD_STATUS) {
                DOWNLOAD_STATUS.add(successMsg);
            }
            System.out.println(successMsg);
        } catch (Exception e) {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            String failMsg = "失?。? + fileName + ",原因:" + e.getMessage();
            synchronized (DOWNLOAD_STATUS) {
                DOWNLOAD_STATUS.add(failMsg);
            }
            System.out.println(failMsg);
        } finally {
            // 關(guān)閉資源
            try {
                if (out != null) out.close();
                if (in != null) in.close();
                if (connection != null) connection.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 批量下載文件
     * @param urlList URL列表
     * @param savePath 保存路徑
     */
    public static void batchDownload(List<String> urlList, String savePath) {
        // 檢查保存路徑是否存在
        File saveDir = new File(savePath);
        if (!saveDir.exists()) {
            saveDir.mkdirs();
        }

        // 提交下載任務(wù)
        for (String url : urlList) {
            EXECUTOR.submit(() -> downloadFile(url, savePath));
        }

        // 關(guān)閉線程池
        EXECUTOR.shutdown();
        try {
            // 等待所有任務(wù)完成
            EXECUTOR.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印下載結(jié)果
        System.out.println("\n===== 下載完成 ======");
        for (String status : DOWNLOAD_STATUS) {
            System.out.println(status);
        }
    }

    public static void main(String[] args) {
        // 測試URL列表(替換為實際可下載的URL)
        List<String> urlList = new ArrayList<>();
        urlList.add("https://example.com/file1.txt");
        urlList.add("https://example.com/file2.jpg");
        urlList.add("https://example.com/file3.pdf");
        urlList.add("https://example.com/file4.zip");
        urlList.add("https://example.com/file5.doc");

        // 批量下載到D盤download目錄
        batchDownload(urlList, "D:\\download");
    }
}

1.7.3 案例總結(jié)

? 這個多線程下載工具綜合運用了線程池、同步機制、IO流等知識,核心亮點:

  1. 使用線程池控制并發(fā)數(shù),避免線程過多導(dǎo)致系統(tǒng)資源耗盡。
  2. 使用 synchronized 保證下載狀態(tài)列表的線程安全。
  3. 采用 CallerRunsPolicy 拒絕策略,當任務(wù)過多時,由調(diào)用線程執(zhí)行,避免任務(wù)丟失。
  4. 完善的資源關(guān)閉和異常處理,保證程序健壯性。

1.8 本章總結(jié)

  1. 線程是進程的執(zhí)行單元,多線程可以提升程序并發(fā)效率,充分利用 CPU 資源。
  2. 線程有三種創(chuàng)建方式:繼承 Thread、實現(xiàn) Runnable、實現(xiàn) Callable,推薦使用后兩種。
  3. 線程的生命周期包含六種狀態(tài),核心是掌握狀態(tài)之間的轉(zhuǎn)換條件。
  4. 并發(fā)安全問題的根源是多個線程操作共享資源,解決方案有 synchronized 同步代碼塊/方法、Lock 鎖。
  5. 線程池可以復(fù)用線程,降低系統(tǒng)開銷,實際開發(fā)中推薦手動創(chuàng)建 ThreadPoolExecutor。
  6. 多線程編程的核心是線程同步資源管理,要注意避免死鎖、內(nèi)存可見性等問題。

到此這篇關(guān)于Java多線程編程怎么學(xué)?線程同步與線程池實戰(zhàn)詳解的文章就介紹到這了,更多相關(guān)JAVA多線程編程實戰(zhàn)教程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java for循環(huán)性能優(yōu)化實現(xiàn)解析

    Java for循環(huán)性能優(yōu)化實現(xiàn)解析

    這篇文章主要介紹了Java for循環(huán)性能優(yōu)化實現(xiàn)解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • 使用java實現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù)

    使用java實現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù)

    這篇文章主要為大家詳細介紹了如何使用java實現(xiàn)備份和恢復(fù)SQLServer表數(shù)據(jù),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • 解決springcloud-gateway限流遇到的問題

    解決springcloud-gateway限流遇到的問題

    這篇文章主要介紹了解決springcloud-gateway限流遇到的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java MongoDB數(shù)據(jù)庫連接方法梳理

    Java MongoDB數(shù)據(jù)庫連接方法梳理

    MongoDB作為一種介于關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫之間的產(chǎn)品,它可以提供可擴展的高性能的數(shù)據(jù)存儲解決方案,近些年來受到了開發(fā)者的喜愛
    2022-08-08
  • SpringBoot對SSL的支持實現(xiàn)

    SpringBoot對SSL的支持實現(xiàn)

    本文主要介紹了SpringBoot對SSL的支持實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08
  • Java 位運算符>>與>>>區(qū)別案例詳解

    Java 位運算符>>與>>>區(qū)別案例詳解

    這篇文章主要介紹了Java 位運算符>>與>>>區(qū)別案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Java操作MongoDB數(shù)據(jù)庫的示例代碼

    Java操作MongoDB數(shù)據(jù)庫的示例代碼

    這篇文章主要介紹了Java操作MongoDB的示例代碼,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下
    2021-04-04
  • Java中將bean放入Spring容器中的幾種方式詳解

    Java中將bean放入Spring容器中的幾種方式詳解

    這篇文章主要介紹了Java中將bean放入Spring容器中的幾種方式詳解,在Spring框架中,有多種方式可以將Bean(即對象)放入Spring容器中,今天我們就來詳細說一下這幾種方式,需要的朋友可以參考下
    2023-07-07
  • Java圖形用戶界面之列表框

    Java圖形用戶界面之列表框

    列表框通過Swing組件JList產(chǎn)生,其總是在屏幕上占據(jù)固定行數(shù)的空間。這篇文章主要介紹了java圖形用戶界面之列表框的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-10-10
  • Java基于drools做規(guī)則校驗的實現(xiàn)

    Java基于drools做規(guī)則校驗的實現(xiàn)

    工作中需要開發(fā)一個規(guī)則服務(wù),提供各種規(guī)則,本文主要介紹了Java基于drools做規(guī)則校驗的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-03-03

最新評論

玉溪市| 红河县| 阳东县| 孟州市| 呼伦贝尔市| 乌拉特后旗| 额敏县| 久治县| 郧西县| 阳山县| 江川县| 格尔木市| 张掖市| 石嘴山市| 建昌县| 赤城县| 惠州市| 济宁市| 内乡县| 德阳市| 辉县市| 赣州市| 呼伦贝尔市| 建昌县| 那曲县| 驻马店市| 宜阳县| 淮滨县| 偃师市| 葵青区| 绥德县| 墨竹工卡县| 昌乐县| 成安县| 郴州市| 绥德县| 青铜峡市| 乌兰察布市| 广汉市| 尤溪县| 通城县|