Java 多線程與并發(fā)編程全面實(shí)戰(zhàn)指南
引言:為什么需要多線程?
在當(dāng)今這個(gè)多核處理器普及的時(shí)代,單線程程序已經(jīng)難以充分利用現(xiàn)代計(jì)算機(jī)硬件的計(jì)算能力。Java 作為一門成熟的企業(yè)級(jí)編程語(yǔ)言,提供了豐富的多線程與并發(fā)編程支持,使開(kāi)發(fā)者能夠構(gòu)建高性能、高并發(fā)的應(yīng)用程序。
多線程編程可以帶來(lái)諸多好處:
- 提高 CPU 利用率:在多核系統(tǒng)上并行執(zhí)行任務(wù)
- 提高程序響應(yīng)性:GUI 應(yīng)用可以保持界面響應(yīng)同時(shí)執(zhí)行后臺(tái)任務(wù)
- 簡(jiǎn)化建模:某些問(wèn)題(如服務(wù)器處理多個(gè)客戶端請(qǐng)求)用多線程模型更自然
- 提高吞吐量:通過(guò)并行處理提高系統(tǒng)整體處理能力
然而,多線程編程也帶來(lái)了復(fù)雜性,包括線程安全、死鎖、競(jìng)態(tài)條件等問(wèn)題。本文將全面介紹 Java 多線程與并發(fā)編程的各個(gè)方面。
一、Java 線程基礎(chǔ)
1.1 線程創(chuàng)建方式
Java 提供了三種基本的線程創(chuàng)建方式:
1.1.1 繼承 Thread 類
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
// 使用
MyThread thread = new MyThread();
thread.start(); // 注意:調(diào)用 run() 是普通方法調(diào)用,start() 才是啟動(dòng)新線程1.1.2 實(shí)現(xiàn) Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable running: " + Thread.currentThread().getName());
}
}
// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();1.1.3 實(shí)現(xiàn) Callable 接口
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Result from " + Thread.currentThread().getName();
}
}
// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());
System.out.println(future.get()); // 獲取返回值
executor.shutdown();最佳實(shí)踐:推薦實(shí)現(xiàn) Runnable 或 Callable 接口,因?yàn)?Java 不支持多重繼承,且這種方式更符合面向?qū)ο蟮脑O(shè)計(jì)原則。
1.2 線程生命周期
Java 線程有以下幾種狀態(tài):
- NEW:新建但未啟動(dòng)
- RUNNABLE:可運(yùn)行狀態(tài)(包括正在運(yùn)行和準(zhǔn)備運(yùn)行)
- BLOCKED:等待監(jiān)視器鎖(同步塊/方法)
- WAITING:無(wú)限期等待(Object.wait()、Thread.join()等)
- TIMED_WAITING:有限期等待(Thread.sleep()、帶超時(shí)的wait/join等)
- TERMINATED:線程執(zhí)行完畢
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println(thread.getState()); // TIMED_WAITING
Thread.sleep(1000);
System.out.println(thread.getState()); // TERMINATED二、線程同步與線程安全
2.1 同步機(jī)制
2.1.1 synchronized 關(guān)鍵字
// 同步方法
public synchronized void syncMethod() {
// 臨界區(qū)代碼
}
// 同步代碼塊
public void someMethod() {
synchronized(this) { // 可以使用任何對(duì)象作為鎖
// 臨界區(qū)代碼
}
}2.1.2 Lock 接口
Lock lock = new ReentrantLock();
public void someMethod() {
lock.lock();
try {
// 臨界區(qū)代碼
} finally {
lock.unlock(); // 確保在finally中釋放鎖
}
}比較:
- synchronized 是 JVM 實(shí)現(xiàn)的,簡(jiǎn)單但功能有限
- Lock 接口提供了更多功能,如嘗試獲取鎖、公平鎖等
2.2 volatile 關(guān)鍵字
volatile 保證變量的可見(jiàn)性,但不保證原子性:
private volatile boolean running = true;
public void stop() {
running = false;
}
public void run() {
while(running) {
// 執(zhí)行任務(wù)
}
}2.3 原子類
java.util.concurrent.atomic 包提供了一系列原子類:
AtomicInteger counter = new AtomicInteger(0); // 線程安全的自增 counter.incrementAndGet(); // CAS 操作 boolean updated = counter.compareAndSet(expect, update);
三、線程通信
3.1 wait/notify 機(jī)制
class SharedResource {
private boolean ready = false;
public synchronized void waitForReady() throws InterruptedException {
while(!ready) {
wait(); // 釋放鎖并等待
}
// 條件滿足,繼續(xù)執(zhí)行
}
public synchronized void setReady() {
ready = true;
notifyAll(); // 通知所有等待線程
}
}3.2 Condition 接口
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}四、并發(fā)工具類
4.1 CountDownLatch
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) {
new Thread(new Worker(startSignal, doneSignal)).start();
}
doSomethingElse(); // 主線程準(zhǔn)備工作
startSignal.countDown(); // 讓所有worker開(kāi)始工作
doSomethingElse();
doneSignal.await(); // 等待所有worker完成4.2 CyclicBarrier
class Solver {
final int N;
final float[][] data;
final CyclicBarrier barrier;
class Worker implements Runnable {
int myRow;
Worker(int row) { myRow = row; }
public void run() {
while (!done()) {
processRow(myRow);
try {
barrier.await();
} catch (InterruptedException ex) {
return;
} catch (BrokenBarrierException ex) {
return;
}
}
}
}
public Solver(float[][] matrix) {
data = matrix;
N = matrix.length;
barrier = new CyclicBarrier(N, () -> {
mergeRows();
});
for (int i = 0; i < N; ++i)
new Thread(new Worker(i)).start();
}
}4.3 Semaphore
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
}五、線程池
5.1 線程池創(chuàng)建
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
5.2 ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心線程數(shù)
10, // 最大線程數(shù)
60, // 空閑線程存活時(shí)間
TimeUnit.SECONDS, // 時(shí)間單位
new ArrayBlockingQueue<>(100), // 工作隊(duì)列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略
);5.3 拒絕策略
- AbortPolicy:默認(rèn)策略,直接拋出 RejectedExecutionException
- CallerRunsPolicy:用調(diào)用者所在線程來(lái)執(zhí)行任務(wù)
- DiscardPolicy:直接丟棄任務(wù)
- DiscardOldestPolicy:丟棄隊(duì)列中最老的任務(wù),然后嘗試提交新任務(wù)
六、并發(fā)集合
6.1 ConcurrentHashMap
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
map.compute("key", (k, v) -> v + 1); // 原子更新6.2 CopyOnWriteArrayList
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item");
// 適合讀多寫(xiě)少的場(chǎng)景6.3 BlockingQueue
BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
// 生產(chǎn)者
queue.put("item");
// 消費(fèi)者
String item = queue.take();七、現(xiàn)代并發(fā)編程
7.1 CompletableFuture
CompletableFuture.supplyAsync(() -> fetchData())
.thenApply(data -> processData(data))
.thenAccept(result -> displayResult(result))
.exceptionally(ex -> {
System.err.println("Error: " + ex.getMessage());
return null;
});7.2 并行流
List<String> results = dataList.parallelStream()
.filter(item -> item.startsWith("A"))
.map(String::toUpperCase)
.collect(Collectors.toList());八、最佳實(shí)踐與常見(jiàn)問(wèn)題
8.1 最佳實(shí)踐
- 優(yōu)先使用高級(jí)并發(fā)工具:如并發(fā)集合、線程池等
- 避免過(guò)度同步:同步范圍越小越好
- 使用不可變對(duì)象:簡(jiǎn)化線程安全設(shè)計(jì)
- 注意資源清理:確保線程池和資源正確關(guān)閉
- 考慮使用無(wú)鎖算法:如原子變量類
8.2 常見(jiàn)問(wèn)題
- 死鎖:多個(gè)線程互相等待對(duì)方釋放鎖
- 避免方法:按固定順序獲取鎖,使用帶超時(shí)的鎖
- 活鎖:線程不斷重試失敗的操作
- 避免方法:引入隨機(jī)退避時(shí)間
- 線程饑餓:某些線程長(zhǎng)期得不到執(zhí)行
- 解決方法:使用公平鎖或調(diào)整線程優(yōu)先級(jí)
- 內(nèi)存可見(jiàn)性問(wèn)題:一個(gè)線程的修改對(duì)另一個(gè)線程不可見(jiàn)
- 解決方法:使用 volatile 或適當(dāng)同步
結(jié)語(yǔ)
Java 多線程與并發(fā)編程是一個(gè)既強(qiáng)大又復(fù)雜的主題。掌握這些技術(shù)可以幫助開(kāi)發(fā)者構(gòu)建高性能、高并發(fā)的應(yīng)用程序,但也需要謹(jǐn)慎處理線程安全和性能問(wèn)題。隨著 Java 版本的更新,并發(fā)編程的 API 也在不斷改進(jìn)和簡(jiǎn)化(如 CompletableFuture、并行流等),開(kāi)發(fā)者應(yīng)當(dāng)持續(xù)學(xué)習(xí)這些新特性。
記住,多線程編程的第一原則是:如果可以不使用多線程,就不要使用多線程。只有在真正需要并行處理或異步操作時(shí),才考慮引入多線程,并且要確保正確處理所有并發(fā)問(wèn)題。
到此這篇關(guān)于Java 多線程與并發(fā)編程全面指南的文章就介紹到這了,更多相關(guān)Java 多線程與并發(fā)編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java與Unix時(shí)間戳的相互轉(zhuǎn)換詳解
這篇文章主要為大家詳細(xì)介紹了Java與Unix時(shí)間戳的相互轉(zhuǎn)換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
JAVA-NIO之Socket/ServerSocket Channel(詳解)
下面小編就為大家?guī)?lái)一篇JAVA-NIO之Socket/ServerSocket Channel(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
idea 有時(shí)提示找不到類或者符號(hào)的解決
這篇文章主要介紹了idea 有時(shí)提示找不到類或者符號(hào)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-09-09
SpringBoot動(dòng)態(tài)修改日志級(jí)別的操作
這篇文章主要介紹了SpringBoot動(dòng)態(tài)修改日志級(jí)別的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java數(shù)據(jù)結(jié)構(gòu)排序算法之歸并排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)排序算法之歸并排序,結(jié)合具體實(shí)例形式詳細(xì)分析了歸并排序的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-05-05
java.net.http.HttpClient使用示例解析
這篇文章主要為大家介紹了java.net.http.HttpClient使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
利用Java+MySQL實(shí)現(xiàn)附近功能實(shí)例
現(xiàn)在很多手機(jī)軟件都用附近搜索功能,但具體是怎么實(shí)現(xiàn)的呢?下面這篇文章就來(lái)給大家介紹關(guān)于利用Java+MySQL實(shí)現(xiàn)附近功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-12-12

