สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง
เปิดใช้สตรีมแบบขนาน ทำความเข้าใจกลุ่มเธรดร่วม และหลีกเลี่ยงข้อผิดพลาดจากสถานะที่เปลี่ยนแปลงได้ร่วมกัน
สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- flatMap สำหรับคอลเลกชันแบบซ้อน
- สตรีมแบบขนาน: ประสิทธิภาพและข้อควรระวัง
- Spliterator: การแบ่งเพื่อการทำงานแบบขนาน
- สตรีมไม่สิ้นสุดด้วย iterate และ generate