JavaRush /จาวาบล็อก /Random-TH /คอฟฟี่เบรค #113. 5 เรื่องที่คุณอาจไม่รู้เกี่ยวกับ Multith...

คอฟฟี่เบรค #113. 5 เรื่องที่คุณอาจไม่รู้เกี่ยวกับ Multithreading ใน Java 10 ส่วนขยาย JetBrains เพื่อต่อสู้กับหนี้ทางเทคนิค

เผยแพร่ในกลุ่ม

5 สิ่งที่คุณอาจไม่รู้เกี่ยวกับ Multithreading ใน Java

ที่มา: DZone Thread เป็นหัวใจสำคัญของภาษาการเขียนโปรแกรม Java แม้แต่การรันโปรแกรม Hello World ก็จำเป็นต้องมีเธรดหลัก หากจำเป็น เราสามารถเพิ่มเธรดอื่นๆ ให้กับโปรแกรมได้หากต้องการให้โค้ดแอปพลิเคชันของเราทำงานได้และมีประสิทธิภาพมากขึ้น หากเรากำลังพูดถึงเว็บเซิร์ฟเวอร์ มันจะประมวลผลคำขอหลายร้อยรายการพร้อมกัน มีการใช้หลายเธรดสำหรับสิ่งนี้ คอฟฟี่เบรค #113.  5 เรื่องที่คุณอาจไม่รู้เกี่ยวกับ Multithreading ใน Java  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();
    }
}
รหัสนี้ดูเหมือนง่าย เรามีอินสแตนซ์ข้อมูลที่ใช้ร่วมกันสองอินสแตนซ์ ( sharedData1และsharedData2 ) ที่ใช้สองเธรด เมื่อเรารันโค้ด เราคาดหวังว่าผลลัพธ์จะเป็นดังนี้:
เธรด 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 Parallelism in Practice , Brian Goetz, หน้า 170) อย่างไรก็ตาม สมมติว่างานต่างๆ ถูกผูกไว้กับ I/O ในกรณีนี้ ขึ้นอยู่กับการสื่อสารภายนอก (เช่น ฐานข้อมูล API อื่นๆ) ดังนั้นจำนวนเธรดที่มากขึ้นจึงสมเหตุสมผล เหตุผลก็คือ เมื่อเธรดกำลังรอ Rest API เธรดอื่นๆ ก็สามารถทำงานต่อไปได้ ทีนี้ลองถามใหม่อีกครั้งว่ามีกี่กระทู้ที่มากเกินไปสำหรับกรณีนี้? พึ่งพา. ไม่มีตัวเลขที่สมบูรณ์แบบที่เหมาะกับทุกกรณี ดังนั้นเราจึงต้องทำการทดสอบอย่างเพียงพอเพื่อค้นหาว่าอะไรทำงานได้ดีที่สุดสำหรับปริมาณงานและการใช้งานเฉพาะของเรา ในสถานการณ์ทั่วไปส่วนใหญ่ เรามักจะมีชุดงานที่หลากหลาย และในกรณีเช่นนี้ สิ่งต่างๆ ก็จะเสร็จสิ้น ในหนังสือของเขา “Java Concurrency in Practice” Brian Goetz เสนอสูตรที่เราสามารถใช้ได้ในกรณีส่วนใหญ่ จำนวนเธรด = จำนวนคอร์ที่มีอยู่ * (1 + เวลารอ / เวลาบริการ) เวลารออาจเป็น IO ได้ เช่น การรอการตอบกลับ HTTP การได้รับการล็อค และอื่นๆ เวลาให้บริการ(เวลาบริการ) คือเวลาในการคำนวณ เช่น การประมวลผลการตอบสนอง HTTP การจัดเรียง/ยกเลิกการจัดเรียง และอื่นๆ ตัวอย่างเช่น แอปพลิเคชันเรียก API แล้วจึงประมวลผล หากเรามีโปรเซสเซอร์ 8 ตัวบนแอปพลิเคชันเซิร์ฟเวอร์ เวลาตอบสนอง API โดยเฉลี่ยคือ 100 มิลลิวินาที และเวลาประมวลผลการตอบสนองคือ 20 มิลลิวินาที ดังนั้นขนาดเธรดในอุดมคติจะเป็น:
ยังไม่มีข้อความ = 8 * ( 1 + 100/20) = 48
อย่างไรก็ตาม นี่เป็นการทำให้ง่ายเกินไป การทดสอบที่เพียงพอเป็นสิ่งสำคัญเสมอในการกำหนดจำนวน

4. มัลติเธรดไม่ใช่ความขนาน

บางครั้งเราใช้มัลติเธรดและความขนานแทนกัน แต่สิ่งนี้ไม่เกี่ยวข้องกันอีกต่อไป แม้ว่าใน Java เราจะทำได้ทั้งสองอย่างโดยใช้เธรด แต่มันก็เป็นสองสิ่งที่แตกต่างกัน “ในการเขียนโปรแกรม มัลติเธรดเป็นกรณีพิเศษโดยไม่คำนึงถึงกระบวนการที่ทำงานอยู่ และความเท่าเทียมคือการดำเนินการคำนวณ (อาจเกี่ยวข้อง) ไปพร้อมๆ กัน Multithreading คือการโต้ตอบกับสิ่งต่างๆ มากมายในเวลาเดียวกัน การเห็นพ้องต้องกันกำลังทำหลายสิ่งหลายอย่างในเวลาเดียวกัน” คำจำกัดความข้างต้นที่กำหนดโดย Rob Pike ค่อนข้างแม่นยำ สมมติว่าเรามีงานอิสระโดยสมบูรณ์ และสามารถคำนวณแยกกันได้ ในกรณีนี้ งานเหล่านี้เรียกว่าแบบขนานและสามารถดำเนินการได้ด้วยพูล Fork/Join หรือเธรดแบบขนาน ในทางกลับกัน ถ้าเรามีหลายงาน บางงานก็อาจต้องอาศัยงานอื่น วิธีที่เราเขียนและจัดโครงสร้างเรียกว่ามัลติเธรด มันเกี่ยวข้องกับโครงสร้างด้วย เราอาจต้องการทำงานหลายอย่างพร้อมกันเพื่อให้ได้ผลลัพธ์ที่แน่นอน โดยไม่จำเป็นต้องทำงานให้เสร็จเร็วขึ้น

5. Project Loom ช่วยให้เราสามารถสร้างเธรดได้นับล้าน

ในประเด็นที่แล้ว ฉันแย้งว่าการมีเธรดมากขึ้นไม่ได้หมายความว่าประสิทธิภาพของแอปพลิเคชันจะดีขึ้น อย่างไรก็ตาม ในยุคของไมโครเซอร์วิส เรามีปฏิสัมพันธ์กับบริการต่างๆ มากเกินไปจนไม่สามารถทำงานเฉพาะด้านได้สำเร็จ ในสถานการณ์ดังกล่าว เธรดยังคงอยู่ในสถานะถูกบล็อกเกือบตลอดเวลา แม้ว่าระบบปฏิบัติการสมัยใหม่จะสามารถรองรับซ็อกเก็ตที่เปิดอยู่นับล้านได้ แต่เราไม่สามารถเปิดช่องทางการสื่อสารได้มากมาย เนื่องจากเราถูกจำกัดด้วยจำนวนเธรด แต่ถ้าคุณสร้างเธรดหลายล้านเธรด และแต่ละเธรดใช้ซ็อกเก็ตแบบเปิดเพื่อสื่อสารกับโลกภายนอกล่ะ สิ่งนี้จะปรับปรุงปริมาณงานแอปพลิเคชันของเราอย่างแน่นอน เพื่อสนับสนุนแนวคิดนี้ มีความคิดริเริ่มใน Java ที่เรียกว่า Project Loom เมื่อใช้มัน เราสามารถสร้างเธรดเสมือนจริงนับล้านได้ ตัวอย่างเช่น เมื่อใช้ข้อมูลโค้ดต่อไปนี้ ฉันสามารถสร้างเธรดได้ 4.5 ล้านเธรดบนเครื่องของฉัน
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 ทีมพัฒนาจำนวนมากรู้สึกกดดันอย่างมากที่จะต้องทำตามกำหนดเวลา ด้วยเหตุนี้พวกเขาจึงมักไม่มีเวลาเพียงพอที่จะแก้ไขและล้างโค้ดเบสของตน บางครั้งในสถานการณ์เหล่านี้ หนี้ทางเทคนิคก็สะสมอย่างรวดเร็ว ส่วนขยายของตัวแก้ไขสามารถช่วยแก้ปัญหานี้ได้ มาดูส่วนขยาย JetBrains ที่ดีที่สุด 10 อันดับเพื่อต่อสู้กับหนี้ทางเทคนิค (พร้อมการรองรับ Java) คอฟฟี่เบรค #113.  5 เรื่องที่คุณอาจไม่รู้เกี่ยวกับ Multithreading ใน Java  10 ส่วนขยาย JetBrains เพื่อต่อสู้กับหนี้ทางเทคนิค - 2

เครื่องมือการปรับโครงสร้างหนี้และทางเทคนิค

1. รีแฟคเตอร์อินไซต์

RefactorInsight ปรับปรุงการมองเห็นการเปลี่ยนแปลงโค้ดใน IDE โดยการให้ข้อมูลเกี่ยวกับการปรับโครงสร้างใหม่
  1. ส่วนขยายกำหนดการปรับโครงสร้างใหม่ในคำขอผสาน
  2. Marks คอมมิตที่มีการรีแฟคเตอร์
  3. ช่วยในการดูการปรับโครงสร้างใหม่ของคอมมิตเฉพาะใดๆ ที่เลือกในแท็บ Git Log
  4. แสดงประวัติการปรับโครงสร้างใหม่ของคลาส วิธีการ และฟิลด์

2. ตัวติดตามปัญหา Stepsize ใน IDE

Stepsize เป็นตัวติดตามปัญหาที่ยอดเยี่ยมสำหรับนักพัฒนา ส่วนขยายนี้ช่วยให้วิศวกรไม่เพียงแต่สร้าง TODO และความคิดเห็นเกี่ยวกับโค้ดที่ดีขึ้นเท่านั้น แต่ยังช่วยจัดลำดับความสำคัญของหนี้ด้านเทคนิค การปรับโครงสร้างใหม่ และอื่นๆ ที่คล้ายกัน:
  1. Stepsize ช่วยให้คุณสร้างและดูงานในโค้ดได้จากในตัวแก้ไข
  2. ค้นหาปัญหาที่ส่งผลต่อฟีเจอร์ที่คุณกำลังทำงานอยู่
  3. เพิ่มปัญหาให้กับ Sprint ของคุณโดยใช้การผสานรวม Jira, Asana, Linear, Azure DevOps และ GitHub

3. CodeStream ที่ระลึกใหม่

New Relic CodeStream เป็นแพลตฟอร์มการทำงานร่วมกันของนักพัฒนาสำหรับการพูดคุยและตรวจสอบโค้ด รองรับการดึงคำขอจาก GitHub, BitBucket และ GitLab การจัดการปัญหาจาก Jira, Trello, Asana และอีก 9 คน และจัดเตรียมการอภิปรายเกี่ยวกับโค้ด โดยเชื่อมโยงทุกอย่างเข้าด้วยกัน
  1. สร้าง ตรวจสอบ และรวมคำขอพุลใน GitHub
  2. รับคำติชมเกี่ยวกับงานที่กำลังดำเนินการด้วยการตรวจสอบโค้ดเบื้องต้น
  3. หารือเกี่ยวกับปัญหาโค้ดกับเพื่อนร่วมทีม

สิ่งที่ต้องทำและความคิดเห็น

4. ปากกาเน้นข้อความความคิดเห็น

ปลั๊กอินนี้ช่วยให้คุณสร้างการเน้นบรรทัดความคิดเห็นและคำหลักภาษาที่กำหนดเองได้ ปลั๊กอินยังมีความสามารถในการกำหนดโทเค็นที่กำหนดเองเพื่อเน้นบรรทัดความคิดเห็น

5. ความคิดเห็นที่ดีขึ้น

ส่วนขยาย Better Comments ช่วยให้คุณสร้างความคิดเห็นที่ชัดเจนยิ่งขึ้นในโค้ดของคุณ ด้วยส่วนขยายนี้ คุณจะสามารถจัดประเภทคำอธิบายประกอบของคุณเป็น:
  1. การแจ้งเตือน
  2. คำขอ
  3. ทำ.
  4. ช่วงเวลาพื้นฐาน

จุดบกพร่องและช่องโหว่ด้านความปลอดภัย

6.โซนาร์ลินท์_

SonarLint ช่วยให้คุณสามารถแก้ไขปัญหาโค้ดก่อนที่จะเกิดขึ้น นอกจากนี้ยังสามารถใช้เป็นเครื่องตรวจตัวสะกดได้อีกด้วย SonarLint เน้นจุดบกพร่องและช่องโหว่ด้านความปลอดภัยในขณะที่คุณเขียนโค้ด พร้อมคำแนะนำในการแก้ไขที่ชัดเจน เพื่อให้คุณสามารถแก้ไขได้ก่อนที่จะคอมมิตโค้ด

7. สปอตบัก

ปลั๊กอิน SpotBugs ให้การวิเคราะห์โค้ดไบต์แบบคงที่เพื่อค้นหาจุดบกพร่องในโค้ด Java จาก IntelliJ IDEA SpotBugs เป็นเครื่องมือตรวจจับข้อบกพร่องสำหรับ Java ที่ใช้การวิเคราะห์แบบคงที่เพื่อค้นหารูปแบบข้อบกพร่องมากกว่า 400 รูปแบบ เช่น การยกเลิกการอ้างอิงตัวชี้ null การวนซ้ำแบบไม่มีที่สิ้นสุด การใช้ไลบรารี Java ในทางที่ผิด และการหยุดชะงัก SpotBugs สามารถระบุข้อบกพร่องร้ายแรงหลายร้อยรายการในแอปพลิเคชันขนาดใหญ่ (โดยทั่วไปประมาณ 1 ข้อบกพร่องต่อ 1,000-2,000 บรรทัดของคำสั่งดิบที่ไม่มีข้อคิดเห็น)

8. เครื่องสแกนช่องโหว่ Snyk

Vulnerability Scanner ของ Snyk ช่วยคุณค้นหาและแก้ไขช่องโหว่ด้านความปลอดภัยและปัญหาคุณภาพของโค้ดในโปรเจ็กต์ของคุณ
  1. การค้นหาและแก้ไขปัญหาด้านความปลอดภัย
  2. ดูรายการปัญหาประเภทต่างๆ โดยแบ่งออกเป็นหมวดหมู่
  3. แสดงคำแนะนำในการแก้ไขปัญหา

9. ตัวระบุอักขระความกว้างเป็นศูนย์

ปลั๊กอินนี้ปรับปรุงการตรวจสอบและการตรวจจับข้อผิดพลาดที่ค้นหาได้ยากที่เกี่ยวข้องกับอักขระความกว้างเป็นศูนย์ที่มองไม่เห็นในซอร์สโค้ดและทรัพยากร เมื่อใช้งาน ตรวจสอบให้แน่ใจว่าได้เปิดใช้งานการตรวจสอบ “อักขระ Unicode ความกว้างเป็นศูนย์” แล้ว

10.รหัสMR _

CodeMR คือเครื่องมือวิเคราะห์โค้ดแบบคงที่และคุณภาพของซอฟต์แวร์ที่ช่วยให้บริษัทซอฟต์แวร์พัฒนาโค้ดและโปรแกรมได้ดีขึ้น CodeMR แสดงภาพตัวชี้วัดโค้ดและคุณลักษณะคุณภาพระดับสูง (การเชื่อมต่อ ความซับซ้อน การเชื่อมโยงกัน และขนาด) ในมุมมองต่างๆ เช่น โครงสร้างแพ็คเกจ, TreeMap, Sunburst, การพึ่งพา และมุมมองกราฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION