พฤติกรรมวิธีคงที่ในสภาพแวดล้อมแบบมัลติเธรดใน java


114

มีคำถามโง่ ๆ ง่ายๆที่กวนใจฉันและมีข้อโต้แย้งมากมายในใจ ฉันต้องการคลายข้อสงสัยทั้งหมดเกี่ยวกับคำถามด้านล่างนี้

class Clstest{

    public static String testStaticMethod(String inFileStr) {

        // section 0

        // section 1

        // do something with inFileStr

        // section 2

        // section 3

        return inFileStr;

    }

}

สมมติว่ามีเธรดห้าเธรดที่เรียกใช้งานClstest.testStaticMethod("arg-n")ในเวลาเดียวกัน

Clstest.testStaticMethod("arg-1")ด้ายสาย 1

เมื่อด้ายที่ 1 คือในส่วนที่ 1, 2 Clstest.testStaticMethod("arg-2")ด้ายสาย

แล้วเธรด 1 จะเกิดอะไรขึ้น? จะเข้าสู่สภาวะหลับใหล?

เมื่อเธรด 1 มีโอกาสจะกลับมาดำเนินการต่อจากส่วนที่ 1 ที่ถูกหยุดชั่วคราวหรือไม่?

จะเกิดขึ้นได้อย่างไรเมื่อมีการแชร์หนึ่งClstest.testStaticMethodและสิ่งเดียวกันClstest.testStaticMethodระหว่างทั้งห้าเธรด

มีความเป็นไปได้ที่จะแลกเปลี่ยนinFileStrเธรดที่ส่งโดยหลายเธรดหรือไม่


คุณกำหนดเป้าหมายเป็นภาษาอะไร
ΩmegaMan

3
@ OmegaMan: มันคือ java
namalfernandolk

คำตอบ:


192

คำตอบของ Hans Passant นั้นดี แต่ฉันคิดว่าฉันจะพยายามอธิบายในระดับที่ง่ายขึ้นเล็กน้อยสำหรับใครก็ตามที่เจอปัญหานี้และยังใหม่กับ Java จัดไปครับ ..

หน่วยความจำใน java แบ่งออกเป็นสองประเภท - ฮีปและสแต็ก ฮีปคือที่ที่อ็อบเจ็กต์ทั้งหมดอาศัยอยู่และสแต็กเป็นที่ที่เธรดทำงานของมัน แต่ละเธรดมีสแต็กของตัวเองและไม่สามารถเข้าถึงสแต็กของกันและกันได้ แต่ละเธรดยังมีตัวชี้ในโค้ดซึ่งชี้ไปที่บิตของโค้ดที่กำลังทำงานอยู่

เมื่อเธรดเริ่มรันเมธอดใหม่มันจะบันทึกอาร์กิวเมนต์และตัวแปรโลคัลในเมธอดนั้นบนสแตกของตัวเอง ค่าเหล่านี้บางค่าอาจเป็นตัวชี้ไปยังวัตถุบนฮีป หากเธรดสองเธรดกำลังเรียกใช้เมธอดเดียวกันในเวลาเดียวกันทั้งคู่จะมีตัวชี้รหัสชี้ไปที่เมธอดนั้นและมีสำเนาอาร์กิวเมนต์และตัวแปรโลคัลของตัวเองบนสแต็ก พวกเขาจะรบกวนกันและกันก็ต่อเมื่อสิ่งของบนกองของพวกเขาชี้ไปที่วัตถุเดียวกันบนกอง ซึ่งในกรณีนี้สิ่งต่างๆอาจเกิดขึ้น แต่ดังที่ฮันส์ชี้ให้เห็นว่าสตริงไม่เปลี่ยนรูป (ไม่สามารถเปลี่ยนแปลงได้) ดังนั้นเราจึงปลอดภัยหากนี่เป็นวัตถุเดียวที่ "แชร์"

เธรดจำนวนมากสามารถใช้วิธีเดียวกันได้ อาจไม่ได้ทำงานพร้อมกัน - ขึ้นอยู่กับจำนวนคอร์ที่คุณมีบนเครื่องของคุณเนื่องจาก JVM แมปเธรด Java กับเธรด OS ซึ่งกำหนดตารางไว้บนเธรดฮาร์ดแวร์ คุณจึงควบคุมวิธีการแทรกสอดของเธรดเหล่านี้ได้เพียงเล็กน้อยโดยไม่ต้องใช้กลไกการซิงโครไนซ์ที่ซับซ้อน

โปรดทราบว่าการนอนหลับเป็นสิ่งที่ด้ายทำกับตัวเอง


3
ดังนั้นในสภาพแวดล้อมตัวประมวลผลแบบมัลติคอร์อาจมีหลายเธรดที่ใช้รหัสเดียวกันในเวลาเดียวกันใช่หรือไม่? และในสภาวะแวดล้อมตัวประมวลผลเดี่ยวจะมีเธรดเดียวเท่านั้นที่ทำงานในช่วงเวลาที่กำหนด (เธรดหลายเธรดที่ใช้เวลาร่วมกัน) ดังนั้นเมื่อเธรด schedular ให้โอกาสจากเธรด excuting ปัจจุบัน (A) ไปยังเธรด (B) เธรด (A) จะทำงานต่อจากที่ที่ถูกหยุดชั่วคราวได้อย่างไร? ฉันหมายถึงมันรู้จุดเรซูเม่ได้อย่างไร? เป็นเพราะ "แต่ละเธรดยังมีตัวชี้ในโค้ดซึ่งชี้ไปที่บิตของโค้ดที่กำลังทำงานอยู่" อย่างที่บอก?
namalfernandolk

6
คุณได้มัน. เพียงเพื่อชี้แจงบางประเด็น - ประการแรกการจัดกำหนดการเธรดนั้นอยู่นอกเหนือการควบคุมของ Java ฉันกำลังพูดถึง Sun's Hotspot JVM ที่นี่ JVM แมปเธรด Java กับเธรด OS และ OS จะตัดสินใจว่าจะรันเธรดใด อย่างที่คุณพูดบนเครื่องคอร์เดียวระบบปฏิบัติการสามารถทำงานได้ทีละเครื่องเท่านั้น แต่ในเครื่องมัลติคอร์อาจทำงานได้มากกว่าหนึ่งเครื่องพร้อมกัน ประการที่สองเธรดไม่ได้ตระหนักถึงเวลาที่ถูกหยุดชั่วคราวข้อมูลเดียวที่มีคือตัวชี้โปรแกรม (ตัวชี้ลงในโค้ด) และสแต็กซึ่งได้รับการบันทึกและเรียกคืนตามที่เป็นจริง
selig

ดังนั้นการรบกวนระหว่างเธรดจึงเกิดขึ้นเมื่อเธรดกำลังใช้ตัวแปรนอกขอบเขตโลคัลและตัวอย่างเช่นเธรดหนึ่งจะอัปเดตค่าของตัวแปรก่อนที่เธรดอื่นจะดึงตัวแปรนั้น (หรือตัวชี้ไปยังตัวแปร) บนสแต็กของตัวเอง? เป็นความเข้าใจที่ถูกต้องหรือไม่?
hariszhr

2
เพื่อให้แม่นยำขึ้นเล็กน้อย ... การรบกวนสามารถเกิดขึ้นได้และอาจเกิดขึ้นได้ แต่ไม่จำเป็นต้องเกิดขึ้น ... เมื่อเธรดแชร์สิ่งต่างๆบนฮีป (ไม่ใช่ในพื้นที่) การรบกวนอาจเกิดขึ้นได้หลายวิธีซึ่งขึ้นอยู่กับการอ้างอิงระหว่างส่วนต่างๆของโค้ด ฉันไม่แน่ใจเกี่ยวกับตัวอย่างของคุณเนื่องจากมีรายละเอียดบางอย่างขาดหายไป บางทีคุณอาจกำลังอ้างถึงปัญหาที่อาจเกิดขึ้นซึ่งเธรด A และ B ทั้งคู่อ่านค่าที่ใช้ร่วมกันจากนั้นทั้งคู่จะอัปเดตตามค่าที่อ่านได้ นี่คือการแข่งขันข้อมูล
Selig

1
@selig ถ้าฉันมีคลาสที่มีเมธอดอินสแตนซ์เท่านั้นสำหรับ ex: คลาสบริการและมันเป็นซิงเกิลตันฉันไม่จำเป็นต้องกังวลเกี่ยวกับเธรดหลายเธรดที่เรียกใช้เมธอดอินสแตนซ์ในแต่ละครั้งเนื่องจากคลาสเซอร์วิสไม่มีสถานะใด ๆ จะปรากฏเฉพาะในกรณีที่คลาสมีตัวแปรอินสแตนซ์ ความเข้าใจของฉันถูกต้องหรือไม่?
Yug Singh

67

จะเข้าสู่สภาวะหลับใหล?

ไม่การรันเธรดจะไม่ส่งผลกระทบต่อเธรดอื่น ๆ ตราบใดที่พวกเขาไม่ได้ตั้งใจซิงโครไนซ์ซึ่งกันและกัน หากคุณมีแกนประมวลผลมากกว่าหนึ่งคอร์เครื่องล่าสุดทั้งหมดมีแนวโน้มที่จะดำเนินการในเวลาเดียวกัน นั่นจะมีโอกาสน้อยลงเล็กน้อยเมื่อคุณเริ่ม 5 เธรดเนื่องจากเครื่องของคุณอาจมีแกนไม่เพียงพอ ระบบปฏิบัติการถูกบังคับให้เลือกระหว่างพวกเขาโดยให้เวลาในการรันแต่ละครั้ง งานของตัวกำหนดตารางเวลาเธรด เธรดจะไม่อยู่ในสถานะ "สลีป" เพียงแค่หยุดชั่วคราวและรอให้ตัวกำหนดตารางเวลาเธรดเปิดโอกาสให้รัน มันจะกลับมาทำงานต่อเมื่อถูกขัดจังหวะโดยตัวกำหนดตารางเวลา

มีความเป็นไปได้ที่จะแลกเปลี่ยน inFileStr ที่ส่งโดยหลายเธรดหรือไม่?

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

ไม่มีการรับประกันดังกล่าวหากอาร์กิวเมนต์เป็นการอ้างอิงถึงวัตถุที่ไม่แน่นอนชนิดอื่น หรือถ้าเมธอดใช้ตัวแปรที่คงที่หรืออ้างอิงถึงอ็อบเจ็กต์บนฮีป จำเป็นต้องมีการซิงโครไนซ์เมื่อเธรดแก้ไขอ็อบเจ็กต์และเธรดอื่นอ่านมัน ล็อคคำหลักในภาษา C # เป็นวิธีต้นแบบในการดำเนินการดังกล่าวจำเป็นต้องมีการประสาน ความจริงที่ว่าวิธีนี้เป็นแบบคงที่ไม่ได้หมายความว่าไม่จำเป็นต้องมีการซิงโครไนซ์ดังกล่าว มีโอกาสน้อยกว่าเนื่องจากคุณไม่ต้องกังวลว่าเธรดจะเข้าถึงวัตถุเดียวกัน (แชร์สิ่งนี้ )


3
อ๊ะไม่เคยเห็นแท็ก [java] ปิดให้เพียงพอ
Hans Passant

ฉันลืมเพิ่มตอนที่โพสต์ ความผิดฉันเอง. :) ยังไงก็ได้ขอบคุณสำหรับคำตอบ มันเป็นประโยชน์มาก
namalfernandolk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.