一文讀懂Java多線程背后的故事
多線程的概念最早可以追溯到計算機科學(xué)的起源之一,也是第一個電子計算機ENIAC的開發(fā)過程中。當(dāng)時,計算速度相對較慢,為了充分利用CPU資源,信息科學(xué)家們開始探索將一個任務(wù)拆分成多個小部分并同時執(zhí)行的方法。這就是多線程的雛形。
1970年代后期,操作系統(tǒng)出現(xiàn)了搶占式調(diào)度策略,這意味著一個進程可以被打斷并轉(zhuǎn)而執(zhí)行另一個進程,實現(xiàn)了不同的程序共享CPU時間。在這種情況下,處理器需要快速地切換上下文并管理多個進程之間的共享資源,多線程編程變得更加重要。
隨著工業(yè)界軟件和互聯(lián)網(wǎng)的迅速發(fā)展,更多的應(yīng)用程序需要支持并發(fā)處理,以達到高效、實時的數(shù)據(jù)處理需求。而多線程編程正好可以提供這種并發(fā)性。
不僅如此,多核處理器技術(shù)的出現(xiàn)使得多線程編程變得更加必要。將單個進程分解成多個線程并在多核處理器上同時運行可以提高整體處理性能。
因此,多線程編程已經(jīng)成為現(xiàn)代計算機編程中重要的一部分,而隨著新型技術(shù)的出現(xiàn),如云計算、大數(shù)據(jù)等,多線程編程的重要性將會越來越突出。
1. 多線程的使用原則
在使用Java多線程的過程中,有一些原則需要遵守,以確保程序的正確性、可靠性和高效性。以下是幾個常見的多線程使用原則:
避免競態(tài)條件:競態(tài)條件是指當(dāng)兩個或更多的線程訪問共享資源時,由于執(zhí)行順序不確定而導(dǎo)致的問題。為了避免競態(tài)條件的發(fā)生,必須要使用同步機制(如synchronized關(guān)鍵字或Lock對象)和互斥量來保證線程的安全性。
最大化利用CPU資源:在多線程程序中,為了達到最高程度的并行ism,應(yīng)該將大任務(wù)分解成小任務(wù)并在多個線程上同時執(zhí)行,以充分利用CPU資源。
避免死鎖:死鎖是指當(dāng)兩個或多個線程相互等待對方的鎖時,導(dǎo)致程序無限期地阻塞的情況。為了避免死鎖,應(yīng)該盡量避免嵌套鎖,以及使用統(tǒng)一的鎖順序來避免死鎖的發(fā)生。
減少上下文切換:上下文切換是指在多線程環(huán)境下,在處理器從一個線程轉(zhuǎn)換到另一個線程時,需要保存當(dāng)前上下文并加載另一個線程的上下文。上下文切換的開銷是很大的,因此應(yīng)該盡量減少上下文切換的次數(shù),例如通過使用線程池來共享線程資源。
避免線程饑餓:線程饑餓是指一個線程由于等待某些資源而無法執(zhí)行的情況。為了避免線程饑餓,可以使用公平鎖來保證所有的線程都有機會獲得資源,或者使用線程優(yōu)先級來控制線程的調(diào)度順序。
以上是多線程使用中的一些常見原則,實際的多線程編程中還有很多需要考慮的問題,需要根據(jù)實際情況進行相應(yīng)的處理。
2. 改善用戶體驗
在 Web 應(yīng)用程序中,用戶希望盡快得到反饋信息,而長時間的等待會導(dǎo)致用戶體驗的下降。多線程可以提高 Web 應(yīng)用程序的響應(yīng)速度,從而改善用戶體驗。例如,當(dāng)用戶請求一個數(shù)據(jù)集合時,可以使用多線程技術(shù)在后臺異步加載數(shù)據(jù),然后立即顯示首頁,同時在數(shù)據(jù)準(zhǔn)備好后再更新頁面。這樣用戶就能立即看到頁面,并在后臺加載完成時獲得更多數(shù)據(jù)。
下面是一個示例,演示了如何使用多線程技術(shù)實現(xiàn)異步加載數(shù)據(jù):
public class DataLoader implements Runnable {
private final DataService dataService;
private final Consumer<Data> onDataLoaded;
public DataLoader(DataService dataService, Consumer<Data> onDataLoaded) {
this.dataService = dataService;
this.onDataLoaded = onDataLoaded;
}
@Override
public void run() {
Data data = dataService.loadData();
onDataLoaded.accept(data);
}
}
public class PageController {
private final DataService dataService;
public PageController(DataService dataService) {
this.dataService = dataService;
}
public void displayPage() {
// 顯示頁面,提供反饋信息
showLoadingIndicator();
// 異步加載數(shù)據(jù)
DataLoader dataLoader = new DataLoader(dataService, data -> {
hideLoadingIndicator();
updatePageWithData(data);
});
Thread thread = new Thread(dataLoader);
thread.start();
}
}在這個代碼示例中,DataLoader 類負(fù)責(zé)異步加載數(shù)據(jù),而 PageController 類負(fù)責(zé)顯示頁面并提供反饋信息。當(dāng) PageController 操作時,它會創(chuàng)建一個新線程來執(zhí)行 DataLoader,然后立即顯示頁面,并在數(shù)據(jù)準(zhǔn)備好后更新頁面。
3. 最大化利用 CPU 資源
使用多線程可以使 CPU 資源得到充分利用,從而提高程序的性能和效率。例如,在計算密集型任務(wù)中,將任務(wù)拆分成多個部分并使用多個線程并行執(zhí)行可以大大縮短任務(wù)完成時間。下面是一個示例,演示了如何在并行環(huán)境下計算斐波那契數(shù)列:
public class FibonacciTask implements Callable<Long> {
private final int n;
public FibonacciTask(int n) {
this.n = n;
}
@Override
public Long call() {
if (n <= 1) {
return (long) n;
} else {
FibonacciTask f1 = new FibonacciTask(n - 1);
FibonacciTask f2 = new FibonacciTask(n - 2);
ForkJoinTask.invokeAll(f1, f2);
return f1.join() + f2.join();
}
}
}
public class FibonacciExample {
public static void main(String[] args) throws Exception {
int n = 50;
FibonacciTask task = new FibonacciTask(n);
long result = ForkJoinPool.commonPool().invoke(task);
System.out.println("Fibonacci number at position " + n + " is " + result);
}
} 在這個代碼示例中,FibonacciTask 類負(fù)責(zé)計算斐波那契數(shù)列。當(dāng) n 大于 1 時,它將任務(wù)分解成兩個子任務(wù)并使用 ForkJoinTask.invokeAll() 方法并行執(zhí)行。FibonacciExample 類負(fù)責(zé)啟動計算,并使用 ForkJoinPool 類中的公共池來執(zhí)行任務(wù)。這樣,就可以將任務(wù)拆分成多個部分并使用多個線程并行執(zhí)行,以最大化利用 CPU 資源。
4. 簡化代碼實現(xiàn)
多線程技術(shù)還可以幫助簡化復(fù)雜的應(yīng)用程序代碼。例如,在面向?qū)ο缶幊讨校绦蛲ǔP枰S護一些狀態(tài),這可能導(dǎo)致代碼變得復(fù)雜和難以理解。使用多線程技術(shù)可以分離狀態(tài)和處理過程,并將處理過程分解成多個線程以提高效率。下面是一個示例,演示了如何使用多線程技術(shù)簡化代碼實現(xiàn)
public class BankAccount {
private int balance;
public synchronized void deposit(int amount) {
balance += amount;
}
public synchronized void withdraw(int amount) {
balance -= amount;
}
public synchronized int getBalance() {
return balance;
}
}
public class TransferTask implements Runnable {
private final BankAccount sourceAccount;
private final BankAccount targetAccount;
private final int amount;
public TransferTask(BankAccount sourceAccount, BankAccount targetAccount, int amount) {
this.sourceAccount = sourceAccount;
this.targetAccount = targetAccount;
this.amount = amount;
}
@Override
public void run() {
sourceAccount.withdraw(amount);
targetAccount.deposit(amount);
}
}
public class BankExample {
public static void main(String[] args) {
BankAccount account1 = new BankAccount();
BankAccount account2 = new BankAccount();
account1.deposit(1000);
TransferTask transferTask1 = new TransferTask(account1, account2, 500);
TransferTask transferTask2 = new TransferTask(account2, account1, 300);
Thread thread1 = new Thread(transferTask1);
Thread thread2 = new Thread(transferTask2);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Account 1 balance: " + account1.getBalance());
System.out.println("Account 2 balance: " + account2.getBalance());
}
}在這個代碼示例中,BankAccount 類負(fù)責(zé)管理銀行賬戶的余額。每個方法都使用 synchronized 關(guān)鍵字來確保方法調(diào)用之間的互斥性。TransferTask 類負(fù)責(zé)執(zhí)行轉(zhuǎn)賬操作。最后,BankExample 類負(fù)責(zé)啟動轉(zhuǎn)賬并輸出賬戶余額。
5. 多線程編程框架
多線程編程的框架通常是一些類或接口的集合,其目的是為了幫助開發(fā)人員更容易地編寫并發(fā)代碼。這些框架提供了高層次的抽象,隱藏了多線程代碼中的復(fù)雜性和細(xì)節(jié),并使得程序員能夠更加專注于業(yè)務(wù)邏輯的實現(xiàn)。
以下是多線程框架中的幾個常見抽象:
線程池:線程池用來管理一組已經(jīng)創(chuàng)建的線程,以便在程序需要時重復(fù)利用它們。線程池的抽象類通常包含上限和下限線程數(shù)、任務(wù)隊列、拒絕策略等屬性,以及方法來提交任務(wù),啟動線程池等。使用線程池可以大大提高系統(tǒng)的效率,降低線程創(chuàng)建與銷毀的開銷。
并發(fā)集合:并發(fā)集合可以讓多個線程安全地訪問同一個集合。Java并發(fā)框架提供了一些并發(fā)集合,例如ConcurrentHashMap、ConcurrentLinkedQueue等。這些集合實現(xiàn)了線程安全的讀寫操作,同時不會導(dǎo)致鎖競爭。
信號量(Semaphore):信號量是一種控制并發(fā)訪問資源的機制,它可以保證在任何時刻,只有指定數(shù)量的線程能夠訪問共享資源。Java并發(fā)框架中的Semaphore類提供了這個機制的實現(xiàn),通過acquire()方法獲取信號量,release()方法釋放信號量。Semaphore可以應(yīng)用于限流、資源控制等場景。
同步器(Synchronizer):同步器是一種比鎖更高級的并發(fā)控制機制,它可以用來實現(xiàn)各種復(fù)雜的同步和互斥需求。Java并發(fā)框架中的ReentrantLock、CountDownLatch和CyclicBarrier等就是同步器的典型實現(xiàn)。
Fork/Join框架:Fork/Join框架是Java 7中引入的并發(fā)框架,它建立在Executor框架之上,特別適合處理計算密集型的任務(wù)。該框架采用分治策略,將大問題拆分成多個小問題,并在多個線程上并行執(zhí)行,最終將結(jié)果合并返回。
多線程編程框架的抽象層次越高,越能夠幫助我們避免寫出低效、高風(fēng)險的代碼。因此,在編寫多線程應(yīng)用程序時,使用已經(jīng)存在的高級別的抽象是一個非常明智的選擇。
6. 總結(jié)
本文介紹了 Java 多線程的實際應(yīng)用場景,包括改善用戶體驗、最大化利用 CPU 資源和簡化代碼實現(xiàn)等。我只是涉及了一些比較基礎(chǔ)的應(yīng)用場景介紹它,還有很多其他領(lǐng)域可以使用 Java 多線程進行優(yōu)化。Java 多線程是 Java 程序員必備的技能之一,深入理解多線程技術(shù)的應(yīng)用場景可以幫助您編寫更高效、更健壯的程序,很開心能夠分享這些。
以上就是一文讀懂Java多線程背后的故事的詳細(xì)內(nèi)容,更多關(guān)于Java多線程的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java?webservice超時時間設(shè)置方法代碼
當(dāng)我們使用WebService進行調(diào)用時,有時會出現(xiàn)超時的情況,下面這篇文章主要給大家介紹了關(guān)于java?webservice超時時間設(shè)置方法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01
常用的ResponseEntity.BodyBuilder和自定義ResponseEntity的實例
這篇文章主要介紹了常用的ResponseEntity.BodyBuilder和自定義ResponseEntity的實例,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
java實現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù)
今天小編就為大家分享一篇java實現(xiàn)讀取txt文件并以在每行以空格取數(shù)據(jù),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
springboot+springsecurity+mybatis+JWT+Redis?實現(xiàn)前后端離實戰(zhàn)教程
這篇文章主要介紹了springboot+springsecurity+mybatis+JWT+Redis?實現(xiàn)前后端離實戰(zhàn)教程,需要的朋友可以參考下2024-01-01
Java線上問題排查神器Arthas實戰(zhàn)原理解析
原先我們Java中我們常用分析問題一般是使用JDK自帶或第三方的分析工具如jstat、jmap、jstack、?jconsole、visualvm、Java?Mission?Control、MAT等,還有一款神器Arthas工具,可幫助程序員解決很多繁瑣的問題,感興趣的朋友一起看看吧2022-01-01

