JavaRush /Java Blog /Random-TW /喝咖啡休息#113。關於 Java 多線程,您可能不知道的 5 件事。解決技術債問題的 10 個 JetBrain...

喝咖啡休息#113。關於 Java 多線程,您可能不知道的 5 件事。解決技術債問題的 10 個 JetBrains 擴展

在 Random-TW 群組發布

關於 Java 多執行緒您可能不知道的 5 件事

來源:DZone 執行緒是 Java 程式語言的核心。即使運行Hello World程式也需要主執行緒。如有必要,如果我們希望應用程式程式碼更具功能性和效能,則可以向程式添加其他線程。如果我們談論的是 Web 伺服器,那麼它會同時處理數百個請求。為此使用多個線程。 喝咖啡休息#113。 關於 Java 多線程,您可能不知道的 5 件事。 解決技術債問題的 10 個 JetBrains 擴充 - 1線程無疑很有用,但對於許多開發人員來說,使用它們可能很困難。在本文中,我將分享新手和經驗豐富的開發人員可能不了解的五個多執行緒概念。

1.程序順序和執行順序不符

當我們編寫程式碼時,我們假設它將完全按照我們編寫的方式執行。然而,實際情況並非如此。如果 Java 編譯器可以確定單執行緒程式碼中的輸出不會改變,則可能會變更執行順序以對其進行最佳化。看下面的程式碼片段:
package ca.bazlur.playground;

import java.util.concurrent.Phaser;

public class ExecutionOrderDemo {
    private static class A {
        int x = 0;
    }

    private static final A sharedData1 = new A();
    private static final A sharedData2 = new A();

    public static void main(String[] args) {
        var phaser = new Phaser(3);
        var t1 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l1 = sharedData1;
            var l2 = l1.x;
            var l3 = sharedData2;
            var l4 = l3.x;
            var l5 = l1.x;
            System.out.println("Thread 1: " + l2 + "," + l4 + "," + l5);
        });
        var t2 = new Thread(() -> {
            phaser.arriveAndAwaitAdvance();
            var l6 = sharedData1;
            l6.x = 3;
            System.out.println("Thread 2: " + l6.x);
        });
        t1.start();
        t2.start();
        phaser.arriveAndDeregister();
    }
}
這段程式碼看起來很簡單。我們有兩個使用兩個執行緒的共享資料實例( sharedData1sharedData2 )。當我們執行程式碼時,我們期望輸出如下:
線程 2: 3 線程 1: 0,0,0
但如果多次運行該程式碼,您將看到不同的結果:
線程 2: 3 線程 1: 3,0,3 線程 2: 3 線程 1: 0,0,3 線程 2: 3 線程 1: 3,3,3 線程 2: 3 線程 1: 0,3,0 線程 2 : 3 線程1: 0,3,3
我並不是說所有這些串流都將在您的電腦上完全像這樣播放,但這完全有可能。

2、Java執行緒數量有限

在 Java 中建立線程很容易。然而,這並不意味著我們可以隨心所欲地創造它們。線程的數量是有限的。我們可以使用以下程式輕鬆找出可以在特定機器上建立多少個執行緒:
package ca.bazlur.playground;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Playground {
    public static void main(String[] args) {
        var counter = new AtomicInteger();
        while (true) {
            new Thread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            }).start();
        }
    }
}
上面的程序非常簡單。它在循環中創建一個線程,然後將其停放,這意味著該線程將被禁用以供將來使用,但會執行系統呼叫並分配記憶體。程式繼續創建線程,直到無法創建更多線程,然後拋出異常。我們感興趣的是在程式拋出異常之前我們將收到的數字。在我的計算機上我只能創建 4065 個線程。

3.太多的執行緒並不能保證更好的效能

相信在 Java 中簡化執行緒就能提高應用程式效能是天真的想法。不幸的是,這個假設對於我們今天 Java 提供的傳統多執行緒模型來說是錯誤的。事實上,太多的線程會降低應用程式的效能。我們首先問這個問題:為了最大化應用程式效能,我們可以創建的最佳最大執行緒數是多少?嗯,答案沒那麼簡單。這在很大程度上取決於我們所做的工作類型。如果我們有多個獨立的任務,所有這些任務都是運算性的並且不會阻塞任何外部資源,那麼擁有大量執行緒並不會提高太多效能。另一方面,如果我們有 8 核心處理器,則最佳執行緒數可能是 (8 + 1)。在這種情況下,我們可以依靠Java 8中引入的平行執行緒。預設情況下,並行執行緒使用共用的Fork/Join池。它創建的執行緒等於可用處理器的數量,這足以讓它們集中工作。為沒有任何阻塞的 CPU 密集型作業添加更多執行緒不會提高效能。相反,我們只會浪費資源。 筆記。擁有額外線程的原因是,即使是計算密集型線程有時也會導致頁面錯誤或由於某些其他原因而被掛起。(參見:實踐中的 Java 並行性,Brian Goetz,第 170 頁)但是,例如,假設任務是 I/O 綁定的。在這種情況下,它們依賴外部通訊(例如資料庫、其他 API),因此較大數量的執行緒是有意義的。原因是當一個執行緒正在等待 Rest API 時,其他執行緒可以繼續工作。現在我們可以再問一次,對於這種情況,多少個執行緒才算太多呢?依靠。不存在適合所有情況的完美數字。因此,我們必須進行充分的測試,找出最適合我們特定工作負載和應用程式的方法。在最典型的場景中,我們通常會有一組混合的任務。在這種情況下,事情就會完成。Brian Goetz 在他的《Java 並發實踐》一書中提出了一個我們在大多數情況下都可以使用的公式。 執行緒數 = 可用核心數 * (1 + 等待時間 / 服務時間) 等待時間可以是 IO,例如等待 HTTP 回應、取得鎖定等。 服務時間(服務時間)是計算時間,例如處理 HTTP 回應、編組/解組等。例如,應用程式呼叫 API,然後對其進行處理。如果我們的應用程式伺服器上有 8 個處理器,平均 API 回應時間為 100ms,回應處理時間為 20ms,那麼理想的執行緒大小為:
N = 8 * ( 1 + 100/20) = 48
然而,這過於簡單化了。充分的測試對於確定數量始終至關重要。

4.多執行緒不是並行

有時我們可以互換使用多執行緒和並行性,但這不再完全相關。儘管在 Java 中我們使用線程來實現這兩者,但它們是兩個不同的東西。「在程式設計中,多執行緒是一種特殊情況,無論進程如何運行,而平行性是同時執行(可能相關的)計算。多線程是指同時與許多事物互動。並發就是同時做很多事情。” Rob Pike 給出的上述定義是相當準確的。假設我們有完全獨立的任務,並且它們可以單獨計算。在這種情況下,這些任務稱為平行任務,可以使用 Fork/Join 池或平行執行緒來執行。另一方面,如果我們有很多任務,其中一些任務可能會依賴其他任務。我們的組合和建構方式稱為多執行緒。和結構有關係。我們可能希望同時執行多項任務以獲得某個結果,但不一定更快完成一項任務。

5.Project Loom 允許我們創建數百萬個線程

在上一點中,我認為擁有更多線程並不意味著提高應用程式效能。然而,在微服務時代,我們與太多的服務互動來完成任何特定的工作。在這種情況下,執行緒大部分時間都處於阻塞狀態。雖然現代作業系統可以處理數百萬個開啟的套接字,但我們無法開啟許多通訊通道,因為我們受到線程數量的限制。但是,如果您創建數百萬個線程,並且每個線程都使用開放的套接字與外界通信,該怎麼辦?這肯定會提高我們的應用程式吞吐量。為了支持這個想法,Java 中有一個名為 Project Loom 的計畫。使用它,我們可以創建數百萬個虛擬線程。例如,使用以下程式碼片段,我能夠在我的電腦上建立 450 萬個執行緒。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;

public class Main {
    public static void main(String[] args) {
        var counter = new AtomicInteger();

        // 4_576_279
        while (true) {
            Thread.startVirtualThread(() -> {
                int count = counter.incrementAndGet();
                System.out.println("thread count = " + count);
                LockSupport.park();
            });
        }
    }
}
要執行此程序,您必須安裝 Java 18,可以在此處下載。您可以使用以下指令執行程式碼: java --source 18 --enable-preview Main.java

解決技術債問題的 10 個 JetBrains 擴展

資料來源:DZone 許多開發團隊都感受到按時完成任務的巨大壓力。因此,他們通常沒有足夠的時間來修復和清理程式碼庫。有時在這些情況下,技術債會迅速累積。編輯器擴充功能可以幫助解決這個問題。讓我們來看看 10 個解決技術債的最佳 JetBrains 擴充(支援 Java)。 喝咖啡休息#113。 關於 Java 多線程,您可能不知道的 5 件事。 解決技術債的 10 個 JetBrains 擴充 - 2

重構和技術債工具

1.重構洞察

RefactorInsight 透過提供有關重構的資訊來提高 IDE 中程式碼變更的可見性。
  1. 此擴充定義了合併請求中的重構。
  2. 標記包含重構的提交。
  3. 協助查看在 Git 日誌標籤中選擇的任何特定提交的重構。
  4. 顯示類別、方法和欄位的重構歷史。

2. IDE 中的 Stepsize 問題追蹤器

Stepsize 對於開發人員來說是一個很棒的問題追蹤器。此擴充不僅可以幫助工程師創建更好的 TODO 和程式碼註釋,還可以優先考慮技術債、重構等:
  1. Stepsize 可讓您直接在編輯器中建立和檢視程式碼中的任務。
  2. 尋找影響您正在開發的功能的問題。
  3. 使用 Jira、Asana、Linear、Azure DevOps 和 GitHub 整合將問題添加到衝刺中。

3.新的Relic CodeStream

New Relic CodeStream 是一個用於討論和審查程式碼的開發人員協作平台。它支援 GitHub、BitBucket 和 GitLab 的拉取請求,支援來自 Jira、Trello、Asana 和其他 9 個平台的問題管理,並提供程式碼討論,將所有這些結合在一起。
  1. 在 GitHub 中建立、審查和合併拉取請求。
  2. 透過初步程式碼審查獲取正在進行的工作的回饋。
  3. 與隊友討論程式碼問題。

TODO 和評論

4.評論螢光筆

該外掛程式可讓您建立註解行和語言關鍵字的自訂突出顯示。該插件還能夠定義自訂標記以突出顯示註解行。

5.更好的評論

Better Comments 擴充功能可協助您在程式碼中建立更清晰的註解。透過此擴展,您將能夠將註釋分類為:
  1. 警報。
  2. 要求。
  3. 去做。
  4. 基本時刻。

錯誤和安全漏洞

6.SonarLint_ _

SonarLint 可讓您在程式碼問題出現之前對其進行故障排除。它也可以用作拼字檢查器。SonarLint 在您編碼時會反白顯示錯誤和安全漏洞,並提供明確的修復說明,以便您可以在提交程式碼之前修復它們。

7.斑點蟲子

SpotBugs 外掛程式提供靜態字節碼分析,以查找 IntelliJ IDEA 中 Java 程式碼中的錯誤。SpotBugs 是一種 Java 缺陷偵測工具,它使用靜態分析來尋找 400 多種錯誤模式,例如空指標取消引用、無限遞歸循環、Java 函式庫的濫用和死鎖。SpotBugs 可以辨識大型應用程式中的數百個嚴重缺陷(通常每 1000-2000 行未註解的原始語句大約有 1 個缺陷)。

8.Snyk漏洞掃描器

Snyk 的漏洞掃描程式可協助您尋找並修復專案中的安全漏洞和程式碼品質問題。
  1. 尋找並修復安全性問題。
  2. 查看按類別劃分的不同類型問題的清單。
  3. 顯示故障排除提示。

9.零寬度字元定位器

該插件增強了對原始程式碼和資源中與不可見零寬度字元相關的難以發現的錯誤的檢查和檢測。使用時,請確保啟用“零寬度unicode字元”檢查。

10.代碼M R_

CodeMR是一款軟體品質和靜態程式碼分析工具,可以幫助軟體公司開發更好的程式碼和程式。CodeMR 在各種視圖(例如套件結構、TreeMap、Sunburst、依賴關係和圖形視圖)中可視化程式碼度量和高階品質屬性(耦合、複雜性、內聚性和大小)。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION