มีคำอธิบายที่ยอดเยี่ยมเกี่ยวกับปัญหานี้โดยAndrei Panginซึ่งลงวันที่ 07 เมษายน 2015 มีให้ที่นี่แต่เขียนเป็นภาษารัสเซีย (ฉันขอแนะนำให้ตรวจสอบตัวอย่างโค้ด - เป็นแบบสากล) ปัญหาทั่วไปคือการล็อกระหว่างการเริ่มต้นคลาส
นี่คือคำพูดบางส่วนจากบทความ:
ตามJLSทุกคลาสมีการล็อกการเริ่มต้นที่ไม่ซ้ำกันซึ่งถูกจับระหว่างการเริ่มต้น เมื่อเธรดอื่นพยายามเข้าถึงคลาสนี้ในระหว่างการเริ่มต้นเธรดจะถูกบล็อกในการล็อกจนกว่าการเตรียมใช้งานจะเสร็จสิ้น เมื่อเริ่มต้นคลาสพร้อมกันมันเป็นไปได้ที่จะหยุดชะงัก
ฉันเขียนโปรแกรมง่ายๆที่คำนวณผลรวมของจำนวนเต็มมันควรพิมพ์อะไร?
public class StreamSum {
static final int SUM = IntStream.range(0, 100).parallel().reduce((n, m) -> n + m).getAsInt();
public static void main(String[] args) {
System.out.println(SUM);
}
}
ตอนนี้ลบparallel()
หรือแทนที่ lambda ด้วยInteger::sum
call - จะเปลี่ยนอะไร
ที่นี่เราเห็นการหยุดชะงักอีกครั้ง [มีตัวอย่างการหยุดชะงักในคลาส initializers ก่อนหน้านี้ในบทความ] เนื่องจากการparallel()
ดำเนินการสตรีมทำงานในเธรดพูลแยกต่างหาก เธรดเหล่านี้พยายามเรียกใช้ lambda body ซึ่งเขียนด้วย bytecode เป็นprivate static
วิธีการภายในStreamSum
คลาส แต่วิธีนี้ไม่สามารถดำเนินการได้ก่อนที่การเริ่มต้นแบบคงที่ของคลาสจะเสร็จสิ้นซึ่งรอผลของการสตรีมเสร็จสิ้น
สิ่งที่ทำให้นึกถึงได้มากขึ้น: โค้ดนี้ทำงานแตกต่างกันในสภาพแวดล้อมที่แตกต่างกัน มันจะทำงานได้อย่างถูกต้องบนเครื่องซีพียูเครื่องเดียวและส่วนใหญ่มักจะค้างบนเครื่อง CPU หลายตัว ความแตกต่างนี้มาจากการใช้งานพูล Fork-Join คุณสามารถตรวจสอบได้เองว่าเปลี่ยนพารามิเตอร์-Djava.util.concurrent.ForkJoinPool.common.parallelism=N