Java 多線程編程核心代碼示例:12 個(gè)場(chǎng)景帶你玩轉(zhuǎn)并發(fā)
還在為多線程編程犯愁嗎?啟動(dòng)線程到底該用start()還是run()?怎么解決線程安全問題?線程池參數(shù)又該怎么設(shè)置才合理?別擔(dān)心!今天這篇文章,我精選了 Java 多線程最常用的 12 個(gè)實(shí)戰(zhàn)場(chǎng)景,從基礎(chǔ)的線程創(chuàng)建到高級(jí)的并發(fā)工具,每個(gè)場(chǎng)景都配有可直接復(fù)制粘貼的核心代碼,看完讓你對(duì)多線程的理解更透徹,并發(fā)編程效率大幅提升!

一、線程創(chuàng)建:開啟多線程的三種方式
1. 繼承 Thread 類創(chuàng)建線程
這是最基礎(chǔ)的線程創(chuàng)建方式,只需重寫run()方法來定義線程任務(wù)。
public class ThreadDemo extends Thread {
@Override
public void run() {
// 線程執(zhí)行的任務(wù)
for (int i = 0; i < 5; i++) {
System.out.println("子線程執(zhí)行:" + i);
try {
Thread.sleep(500); // 模擬耗時(shí)操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread thread = new ThreadDemo();
thread.start(); // 啟動(dòng)線程(必須調(diào)用start(),而非直接調(diào)用run())
// 主線程執(zhí)行
for (int i = 0; i < 5; i++) {
System.out.println("主線程執(zhí)行:" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}關(guān)鍵坑點(diǎn):調(diào)用start()才會(huì)真正創(chuàng)建線程,讓線程進(jìn)入就緒狀態(tài)等待 CPU 調(diào)度;直接調(diào)用run()只是普通的方法調(diào)用,不會(huì)實(shí)現(xiàn)并發(fā)執(zhí)行。
2. 實(shí)現(xiàn) Runnable 接口創(chuàng)建線程
這種方式能避免單繼承的限制,是更推薦的線程創(chuàng)建方式。
public class RunnableDemo implements Runnable {
@Override
public void run() {
// 線程任務(wù)
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
// 創(chuàng)建任務(wù)對(duì)象
Runnable task = new RunnableDemo();
// 傳入線程并啟動(dòng)
Thread thread = new Thread(task, "自定義線程名");
thread.start(); // 輸出:當(dāng)前線程:自定義線程名
}
}優(yōu)勢(shì):一個(gè)任務(wù)可以被多個(gè)線程共享,非常適合多線程處理同一資源的場(chǎng)景,比如賣票系統(tǒng)。
3. 實(shí)現(xiàn) Callable 接口創(chuàng)建帶返回值的線程
當(dāng)需要獲取線程執(zhí)行結(jié)果時(shí),就可以使用這種方式,配合Future來接收返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
// 計(jì)算并返回結(jié)果
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = new CallableDemo();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
// 獲取線程返回值(會(huì)阻塞直到任務(wù)完成)
Integer result = futureTask.get();
System.out.println("1-100的和:" + result); // 輸出:5050
}
}使用場(chǎng)景:異步計(jì)算(如下單后異步生成訂單報(bào)表)、需要返回結(jié)果的多線程任務(wù)等。
二、線程控制:狀態(tài)管理與協(xié)作
4. 線程休眠與中斷
sleep()方法能讓線程休眠,interrupt()方法可以中斷線程(但這并非強(qiáng)制,需要配合處理)。
public class ThreadSleepDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("線程開始休眠10秒...");
Thread.sleep(10000); // 休眠10秒
System.out.println("線程休眠結(jié)束");
} catch (InterruptedException e) {
// 捕獲中斷異常,執(zhí)行中斷后的處理
System.out.println("線程被中斷!");
return; // 退出線程
}
});
thread.start();
// 主線程5秒后中斷子線程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 中斷子線程
}
}執(zhí)行結(jié)果:子線程休眠 5 秒后就會(huì)被中斷,不會(huì)執(zhí)行到 “休眠結(jié)束” 這一步。
5. 線程等待與喚醒(wait/notify)
這是線程間協(xié)作的經(jīng)典方式,而且必須在同步代碼塊中使用。
public class WaitNotifyDemo {
private static final Object lock = new Object();
private static boolean flag = false;
public static void main(String[] args) {
// 等待線程
new Thread(() -> {
synchronized (lock) {
while (!flag) { // 用while防止虛假喚醒
try {
System.out.println("條件不滿足,等待...");
lock.wait(); // 釋放鎖并等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("條件滿足,執(zhí)行后續(xù)操作");
}
}).start();
// 喚醒線程
new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000); // 模擬準(zhǔn)備工作
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("條件已滿足,喚醒等待線程");
lock.notify(); // 喚醒一個(gè)等待線程(notifyAll()喚醒所有)
}
}).start();
}
}避坑點(diǎn):wait()必須放在while循環(huán)中判斷條件,不能用if,否則可能出現(xiàn)虛假喚醒(即被喚醒后條件仍不滿足)。
三、線程安全:避免并發(fā)問題的關(guān)鍵
6. synchronized 同步方法與代碼塊
這是最常用的線程安全解決方案,能保證同一時(shí)間只有一個(gè)線程執(zhí)行同步代碼。
public class SynchronizedDemo {
private int count = 0;
private static int staticCount = 0;
// 同步實(shí)例方法(鎖是當(dāng)前對(duì)象)
public synchronized void increment() {
count++;
}
// 同步靜態(tài)方法(鎖是類對(duì)象)
public static synchronized void staticIncrement() {
staticCount++;
}
// 同步代碼塊(鎖可以是任意對(duì)象)
public void syncBlock() {
synchronized (this) { // 鎖當(dāng)前對(duì)象
count++;
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo demo = new SynchronizedDemo();
// 多線程執(zhí)行10000次自增
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
demo.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) {
demo.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最終結(jié)果:" + demo.count); // 一定是20000(無同步則可能小于20000)
}
}適用場(chǎng)景:簡(jiǎn)單的線程安全需求,如計(jì)數(shù)器、資源訪問控制等。
7. Atomic 原子類:無鎖化線程安全
java.util.concurrent.atomic包下的類,通過 CAS 機(jī)制實(shí)現(xiàn)線程安全,性能優(yōu)于synchronized。
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicDemo {
// 原子整數(shù),替代int的線程安全版本
private static AtomicInteger atomicCount = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
// 10個(gè)線程各自自增1000次
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
atomicCount.incrementAndGet(); // 原子自增(替代count++)
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("最終結(jié)果:" + atomicCount.get()); // 一定是10000
}
}常用原子類:AtomicInteger、AtomicLong、AtomicBoolean、AtomicReference(原子引用對(duì)象)。
四、線程池:高效管理線程資源
8. 線程池基礎(chǔ):創(chuàng)建與使用
線程池能夠復(fù)用線程、控制并發(fā)數(shù),避免頻繁創(chuàng)建銷毀線程帶來的開銷。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 創(chuàng)建固定大小的線程池(核心線程數(shù)=最大線程數(shù)=5)
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交10個(gè)任務(wù)
for (int i = 0; i < 10; i++) {
int taskNum = i;
executor.submit(() -> {
System.out.println("執(zhí)行任務(wù)" + taskNum + ",線程:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown(); // 關(guān)閉線程池(不再接收新任務(wù),等待現(xiàn)有任務(wù)完成)
}
}為什么不用new Thread()而用線程池?
- 減少線程創(chuàng)建銷毀的性能開銷
- 控制最大并發(fā)數(shù),避免線程過多導(dǎo)致的資源耗盡
- 便于管理和監(jiān)控線程狀態(tài)
9. 線程池參數(shù)詳解與自定義
要掌握ThreadPoolExecutor的核心參數(shù),避免使用Executors默認(rèn)創(chuàng)建(可能導(dǎo)致 OOM)。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class CustomThreadPoolDemo {
public static void main(String[] args) {
// 自定義線程池(推薦方式)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心線程數(shù)(常駐線程)
5, // 最大線程數(shù)
60, // 空閑線程存活時(shí)間
TimeUnit.SECONDS, // 時(shí)間單位
new ArrayBlockingQueue<>(10), // 任務(wù)隊(duì)列(容量10)
new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略(讓提交任務(wù)的線程執(zhí)行)
);
// 提交任務(wù)...
for (int i = 0; i < 20; i++) {
int num = i;
executor.submit(() -> {
System.out.println("任務(wù)" + num + "執(zhí)行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
}
}核心參數(shù)設(shè)置原則:
- 核心線程數(shù):根據(jù) CPU 核心數(shù)和任務(wù)類型調(diào)整(CPU 密集型 = 核心數(shù) + 1,IO 密集型 = 核心數(shù) * 2)
- 隊(duì)列容量:避免無界隊(duì)列(如LinkedBlockingQueue默認(rèn)無界,可能堆積大量任務(wù)導(dǎo)致 OOM)
- 拒絕策略:根據(jù)業(yè)務(wù)選擇(丟棄、拋出異常、讓提交者執(zhí)行等)
五、高級(jí)并發(fā)工具:提升并發(fā)編程效率
10. CountDownLatch:等待多線程完成
讓主線程等待多個(gè)子線程都執(zhí)行完畢后再繼續(xù)。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
int threadCount = 3;
// 初始化計(jì)數(shù)器為3
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println("子線程執(zhí)行中...");
Thread.sleep(2000); // 模擬任務(wù)
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 計(jì)數(shù)器減1
System.out.println("子線程執(zhí)行完畢,計(jì)數(shù)器-1");
}
}).start();
}
System.out.println("等待所有子線程完成...");
latch.await(); // 等待計(jì)數(shù)器變?yōu)?
System.out.println("所有子線程已完成,主線程繼續(xù)執(zhí)行");
}
}使用場(chǎng)景:并發(fā)測(cè)試(如等待所有測(cè)試線程準(zhǔn)備就緒)、批量任務(wù)匯總結(jié)果等。
11. CyclicBarrier:多線程同步等待
讓多個(gè)線程到達(dá)屏障點(diǎn)后再一起繼續(xù)執(zhí)行(可重復(fù)使用)。
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
int parties = 3; // 參與的線程數(shù)
// 屏障點(diǎn)動(dòng)作:所有線程到達(dá)后執(zhí)行
CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
System.out.println("所有線程已到達(dá)屏障點(diǎn),開始一起執(zhí)行!");
});
for (int i = 0; i < parties; i++) {
int threadNum = i;
new Thread(() -> {
try {
System.out.println("線程" + threadNum + "正在執(zhí)行任務(wù)...");
Thread.sleep((threadNum + 1) * 1000); // 模擬不同耗時(shí)的任務(wù)
System.out.println("線程" + threadNum + "到達(dá)屏障點(diǎn),等待其他線程");
barrier.await(); // 等待其他線程到達(dá)
System.out.println("線程" + threadNum + "繼續(xù)執(zhí)行后續(xù)操作");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}使用場(chǎng)景:多階段任務(wù)(如分布式計(jì)算中,所有節(jié)點(diǎn)完成第一階段后再開始第二階段)。
12. Semaphore:控制并發(fā)訪問數(shù)量
類似于 “信號(hào)燈”,控制同時(shí)訪問某個(gè)資源的線程數(shù)量。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
int permits = 2; // 允許同時(shí)訪問的線程數(shù)
Semaphore semaphore = new Semaphore(permits);
// 5個(gè)線程競(jìng)爭(zhēng)訪問資源
for (int i = 0; i < 5; i++) {
int threadNum = i;
new Thread(() -> {
try {
semaphore.acquire(); // 獲取許可(如果沒有則等待)
System.out.println("線程" + threadNum + "獲取到許可,正在訪問資源");
Thread.sleep(2000); // 模擬訪問資源的耗時(shí)
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("線程" + threadNum + "釋放許可");
semaphore.release(); // 釋放許可
}
}).start();
}
}
}使用場(chǎng)景:限流(如控制同時(shí)訪問數(shù)據(jù)庫(kù)的連接數(shù))、資源池(如線程池、連接池)等。



























