0Pricing
Java Academy · บทเรียน

สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง

เปิดใช้สตรีมแบบขนาน ทำความเข้าใจกลุ่มเธรดร่วม และหลีกเลี่ยงข้อผิดพลาดจากสถานะที่เปลี่ยนแปลงได้ร่วมกัน

สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

การเปิดใช้งานสตรีมแบบขนาน

เรียกใช้ .parallel() กับสตรีมใดก็ได้ หรือใช้ Collection.parallelStream() การดำเนินการของสตรีมจะทำงานบน ForkJoinPool ที่ใช้ร่วมกัน (ค่าเริ่มต้น: คอร์ CPU - 1 เธรด)

long count = list.parallelStream()
    .filter(n -> n % 2 == 0)
    .count();
System.out.println(count);

สตรีมแบบขนานจะคุ้มค่าเมื่อใด

สตรีมแบบขนานจะคุ้มค่าเมื่อชุดข้อมูลมีขนาดใหญ่ (มีองค์ประกอบตั้งแต่ 100,000 รายการขึ้นไป) การดำเนินการกับแต่ละองค์ประกอบใช้การคำนวณมาก และลำดับการประมวลผลไม่มีสถานะและไม่ขึ้นกับลำดับ

// Good candidate: CPU-heavy computation on large dataset
long sum = LongStream.rangeClosed(1, 10_000_000)
    .parallel()
    .filter(n -> isPrime(n))
    .sum();
System.out.println(sum);

เมื่อไม่ควรใช้สตรีมแบบขนาน

หลีกเลี่ยงสตรีมแบบขนานสำหรับคอลเลกชันขนาดเล็ก การดำเนินการที่จำกัดด้วย I/O (ซึ่งบล็อกเธรดของ ForkJoinPool) การดำเนินการที่มีสถานะ (การเรียงลำดับ, distinct) หรือเมื่อจำเป็นต้องรักษาลำดับและการกู้คืนลำดับนั้นมีค่าใช้จ่ายสูง

// Bad: I/O bound — blocking ForkJoinPool threads starves other tasks
List<String> result = urls.parallelStream()
    .map(url -> httpGet(url)) // BLOCKS the common pool
    .collect(Collectors.toList());

ข้อบกพร่องจากสถานะที่เปลี่ยนแปลงได้และใช้ร่วมกัน

สตรีมแบบขนานจะดำเนินการบนหลายเธรดพร้อมกัน การแก้ไขคอลเลกชันที่เปลี่ยนแปลงได้และใช้ร่วมกันทำให้เกิดสภาวะแข่งขันของข้อมูลและผลลัพธ์ไม่ถูกต้อง

// RACE CONDITION — never do this:
List<Integer> results = new ArrayList<>(); // not thread-safe
numbers.parallelStream().forEach(n -> results.add(n)); // corrupts list!
// Fix:
List<Integer> safe = numbers.parallelStream().collect(Collectors.toList());

การดำเนินการที่มีสถานะ: sorted และ distinct

sorted() และ distinct() ต้องเห็นองค์ประกอบทั้งหมดก่อนสร้างผลลัพธ์ ซึ่งจำกัดการประมวลผลแบบขนาน และมักทำให้แบบขนานช้ากว่าแบบทำงานตามลำดับ

// sorted() forces collect-all, then sort — parallel overhead usually not worth it:
list.parallelStream().sorted().collect(Collectors.toList());

การดำเนินการที่ขึ้นกับลำดับ

findFirst() และ forEachOrdered() จะรักษาลำดับการพบในสตรีมแบบขนาน ซึ่งเพิ่มค่าใช้จ่ายในการประสานงาน หากลำดับไม่สำคัญ ให้ใช้ findAny() หรือ forEach()

// Faster in parallel (order-insensitive):
Optional<Integer> any = list.parallelStream().filter(n -> n > 10).findAny();
// Slower in parallel (must preserve order):
Optional<Integer> first = list.parallelStream().filter(n -> n > 10).findFirst();

การเลือกขนาดกลุ่มเธรด

ForkJoinPool ที่ใช้ร่วมกันจะใช้ Runtime.getRuntime().availableProcessors() - 1 เธรด เรียกใช้สตรีมแบบขนานบนกลุ่มที่กำหนดเองโดยส่งงานเข้าไปภายในคำสั่ง ForkJoinPool.invoke()

ForkJoinPool custom = new ForkJoinPool(8);
long result = custom.submit(
    () -> LongStream.rangeClosed(1, 1_000_000).parallel().sum()
).get();

การวัดประสิทธิภาพแบบขนานเทียบกับแบบลำดับ

ควรวัดประสิทธิภาพด้วย JMH โดยใช้ขนาดข้อมูลที่ใกล้เคียงกับการใช้งานจริงเสมอ ค่าใช้จ่ายของการประมวลผลแบบขนาน (การแบ่ง การประสานงานของเธรด และการรวมผล) จะคุ้มก็ต่อเมื่อเวลาในการคำนวณมากกว่าค่าใช้จ่ายดังกล่าวอย่างมาก

@Benchmark
public long sequential() { return LongStream.rangeClosed(1,1_000_000).sum(); }
@Benchmark
public long parallel()   { return LongStream.rangeClosed(1,1_000_000).parallel().sum(); }

การดำเนินการลดรูปด้วยการประมวลผลแบบขนาน

reduce() และ collect() ออกแบบมาให้ทำงานแบบขนานได้อย่างถูกต้อง เมื่อการดำเนินการมีสมบัติการเปลี่ยนหมู่และค่าเอกลักษณ์ถูกต้อง

// Associative reduce — safe in parallel:
int sum = list.parallelStream().reduce(0, Integer::sum);
// Non-associative: subtraction — NOT safe in parallel:
int bad = list.parallelStream().reduce(0, (a, b) -> a - b); // wrong result!

ความสามารถในการแบ่งมีความสำคัญ

สตรีมแบบขนานจะแบ่งแหล่งข้อมูลโดยใช้ Spliterator ArrayList และอาร์เรย์แบ่งได้ในเวลา O(1) ส่วน LinkedList และ HashSet แบ่งได้ไม่ดี จึงลดประสิทธิภาพของการประมวลผลแบบขนาน

สรุป: รายการตรวจสอบสตรีมแบบขนาน

ก่อนใช้การประมวลผลแบบขนาน: (1) ชุดข้อมูลขนาดใหญ่ (2) การดำเนินการที่ใช้ CPU (3) ไม่มีสถานะที่เปลี่ยนแปลงได้และใช้ร่วมกัน (4) ไม่ขึ้นกับลำดับ (5) แหล่งข้อมูลที่แบ่งได้ (อาร์เรย์/ArrayList) หากไม่แน่ใจ ให้วัดประสิทธิภาพ

ตรวจสอบความเข้าใจอย่างรวดเร็ว

จะเกิดอะไรขึ้นเมื่อคุณเพิ่มข้อมูลลงในคอลเลกชันที่ไม่ปลอดภัยต่อเธรดภายใน forEach ของสตรีมแบบขนาน

ทบทวน

สตรีมแบบขนานใช้ ForkJoinPool และมีประสิทธิภาพสำหรับลำดับการประมวลผลขนาดใหญ่ที่ใช้ CPU ไม่ขึ้นกับลำดับ และไร้สถานะ ห้ามเปลี่ยนแปลงสถานะที่ใช้ร่วมกันโดยเด็ดขาด ควรวัดประสิทธิภาพก่อนตัดสินใจใช้ — สำหรับข้อมูลขนาดเล็ก แบบขนานมักช้ากว่า

คำถามที่พบบ่อย

บทเรียน “สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง”

เปิดใช้สตรีมแบบขนาน ทำความเข้าใจกลุ่มเธรดร่วม และหลีกเลี่ยงข้อผิดพลาดจากสถานะที่เปลี่ยนแปลงได้ร่วมกัน คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง” ใช้เวลานานแค่ไหน

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

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. flatMap สำหรับคอลเลกชันแบบซ้อน
  2. สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง
  3. Spliterator: การแบ่งเพื่อการทำงานแบบขนาน
  4. สตรีมไม่สิ้นสุดด้วย iterate และ generate
← กลับไปที่ Java Academy