ฉันกำลังอ่านมัลติเธรดใน Java และฉันเจอสิ่งนี้
ตัวแปรโลคัลคือเธรดที่ปลอดภัยใน Java
ตั้งแต่นั้นมาฉันก็คิดว่าตัวแปรในเครื่องมีความปลอดภัยอย่างไร / ทำไม
ใครช่วยแจ้งให้เราทราบได้
ฉันกำลังอ่านมัลติเธรดใน Java และฉันเจอสิ่งนี้
ตัวแปรโลคัลคือเธรดที่ปลอดภัยใน Java
ตั้งแต่นั้นมาฉันก็คิดว่าตัวแปรในเครื่องมีความปลอดภัยอย่างไร / ทำไม
ใครช่วยแจ้งให้เราทราบได้
คำตอบ:
เมื่อคุณสร้างเธรดจะมีการสร้างสแต็กของตัวเอง เธรดสองเธรดจะมีสองสแต็กและเธรดหนึ่งเธรดจะไม่แชร์สแต็กกับเธรดอื่น
ตัวแปรโลคัลทั้งหมดที่กำหนดไว้ในโปรแกรมของคุณจะได้รับการจัดสรรหน่วยความจำในสแต็ก (ตามที่ Jatin แสดงความคิดเห็นหน่วยความจำในที่นี้หมายถึงค่าอ้างอิงสำหรับอ็อบเจ็กต์และค่าสำหรับประเภทดั้งเดิม) (แต่ละวิธีการเรียกโดยเธรดจะสร้างสแต็กเฟรมบนสแต็กของตัวเอง) ทันทีที่เธรดนี้ดำเนินการเมธอดเสร็จสแต็กเฟรมจะถูกลบออก
มีการบรรยายที่ยอดเยี่ยมโดยศาสตราจารย์ Stanford ใน youtubeซึ่งอาจช่วยคุณในการทำความเข้าใจแนวคิดนี้
ตัวแปรภายในจะถูกเก็บไว้ในสแต็กของแต่ละเธรด นั่นหมายความว่าจะไม่มีการแชร์ตัวแปรภายในระหว่างเธรด นั่นหมายความว่าตัวแปรดั้งเดิมในพื้นที่ทั้งหมดมีเธรดที่ปลอดภัย
public void someMethod(){
long threadSafeInt = 0;
threadSafeInt++;
}
การอ้างอิงถึงวัตถุในท้องถิ่นนั้นแตกต่างกันเล็กน้อย ไม่มีการแชร์ข้อมูลอ้างอิง อย่างไรก็ตามอ็อบเจ็กต์ที่อ้างถึงจะไม่ถูกเก็บไว้ในโลคัลสแต็กของแต่ละเธรด วัตถุทั้งหมดจะถูกเก็บไว้ในฮีปที่แชร์ หากอ็อบเจ็กต์ที่สร้างขึ้นภายในไม่เคยหนีเมธอดที่สร้างขึ้นแสดงว่าเธรดปลอดภัย ในความเป็นจริงคุณสามารถส่งต่อไปยังเมธอดและอ็อบเจ็กต์อื่น ๆ ได้ตราบเท่าที่ไม่มีเมธอดหรืออ็อบเจ็กต์เหล่านี้ทำให้อ็อบเจ็กต์ที่ส่งผ่านพร้อมใช้งานสำหรับเธรดอื่น
ลองนึกถึงวิธีการเช่นคำจำกัดความของฟังก์ชันการทำงาน เมื่อเธรดสองเธรดเรียกใช้วิธีการเดียวกันจะไม่มีความเกี่ยวข้องกัน พวกเขาแต่ละคนจะสร้างตัวแปรท้องถิ่นแต่ละเวอร์ชันของตนเองและจะไม่สามารถโต้ตอบกันได้ไม่ว่าด้วยวิธีใด
หากตัวแปรไม่ได้อยู่ในพื้นที่ (เช่นตัวแปรอินสแตนซ์ที่กำหนดไว้นอกเมธอดในระดับคลาส) ตัวแปรเหล่านั้นจะถูกแนบเข้ากับอินสแตนซ์ (ไม่ใช่การเรียกใช้เมธอดเพียงครั้งเดียว) ในกรณีนี้เธรดสองเธรดที่ใช้วิธีการเดียวกันทั้งสองจะเห็นตัวแปรเดียวและสิ่งนี้ไม่ปลอดภัยสำหรับเธรด
พิจารณาสองกรณีนี้:
public class NotThreadsafe {
int x = 0;
public int incrementX() {
x++;
return x;
}
}
public class Threadsafe {
public int getTwoTimesTwo() {
int x = 1;
x++;
return x*x;
}
}
ในครั้งแรกเธรดสองเธรดที่ทำงานบนอินสแตนซ์เดียวกันNotThreadsafe
จะเห็น x เดียวกัน สิ่งนี้อาจเป็นอันตรายเนื่องจากเธรดกำลังพยายามเปลี่ยน x! ในวินาทีที่สองเธรดสองเธรดที่ทำงานบนอินสแตนซ์เดียวกันThreadsafe
จะเห็นตัวแปรที่แตกต่างกันโดยสิ้นเชิงและไม่สามารถส่งผลกระทบซึ่งกันและกัน
การเรียกใช้แต่ละเมธอดมีตัวแปรภายในของตัวเองและเห็นได้ชัดว่าการเรียกใช้เมธอดเกิดขึ้นในเธรดเดียว ตัวแปรที่อัปเดตโดยเธรดเดียวเท่านั้นที่จะปลอดภัยต่อเธรดโดยเนื้อแท้
อย่างไรก็ตามโปรดจับตาดูความหมายของสิ่งนี้อย่างใกล้ชิดมีเพียงการเขียนถึงตัวแปรเท่านั้นที่ปลอดภัยต่อเธรด การเรียกใช้เมธอดบนอ็อบเจ็กต์ที่อ้างถึงนั้นไม่ปลอดภัยต่อเธรดโดยเนื้อแท้ เช่นเดียวกันกับการอัปเดตตัวแปรของวัตถุโดยตรง
นอกจากคำตอบอื่น ๆ เช่น Nambari's
ฉันต้องการชี้ให้เห็นว่าคุณสามารถใช้ตัวแปรท้องถิ่นในวิธีการประเภท anoymous:
เมธอดนี้สามารถเรียกใช้ในเธรดอื่นซึ่งอาจทำให้เธรดปลอดภัยดังนั้น java จึงบังคับให้ตัวแปรโลคัลทั้งหมดที่ใช้ในประเภท anoymous ถูกประกาศเป็นขั้นสุดท้าย
พิจารณารหัสที่ผิดกฎหมายนี้:
public void nonCompilableMethod() {
int i=0;
for(int t=0; t<100; t++)
{
new Thread(new Runnable() {
public void run() {
i++; //compile error, i must be final:
//Cannot refer to a non-final variable i inside an
//inner class defined in a different method
}
}).start();
}
}
หาก java อนุญาตสิ่งนี้ (เช่น C # ทำผ่าน "การปิด") ตัวแปรภายในจะไม่ปลอดภัยในทุกสถานการณ์อีกต่อไป ในกรณีนี้ค่าของในตอนท้ายของกระทู้ทั้งหมดที่ไม่ได้รับประกันว่าจะi
100
เธรดจะมีกองของตัวเอง เธรดสองเธรดจะมีสองสแต็กและเธรดหนึ่งเธรดจะไม่แชร์สแต็กกับเธรดอื่น ตัวแปรภายในจะถูกเก็บไว้ในสแต็กของแต่ละเธรด นั่นหมายความว่าจะไม่มีการแชร์ตัวแปรภายในระหว่างเธรด
โดยทั่วไปมีพื้นที่เก็บข้อมูลสี่ประเภทใน java เพื่อจัดเก็บข้อมูลคลาสและข้อมูล:
พื้นที่วิธี, กอง, กอง JAVA, พีซี
ดังนั้นพื้นที่วิธีการและฮีปจะถูกแชร์โดยเธรดทั้งหมด แต่ทุกเธรดจะมี JAVA Stack และ PC ของตัวเองและเธรดอื่น ๆ จะไม่แชร์
แต่ละวิธีใน java เป็นแบบ Stack frame ดังนั้นเมื่อเมธอดหนึ่งถูกเรียกโดยเธรดที่สแต็กเฟรมถูกโหลดบน JAVA Stack ตัวแปรโลคัลทั้งหมดที่อยู่ในสแต็กเฟรมนั้นและสแต็กตัวถูกดำเนินการที่เกี่ยวข้องจะไม่ถูกแชร์โดยผู้อื่น พีซีจะมีข้อมูลของคำสั่งถัดไปเพื่อดำเนินการในรหัสไบต์ของวิธีการ ดังนั้นตัวแปรท้องถิ่นทั้งหมดคือ THREAD SAFE
@ เวสตันยังให้คำตอบที่ดี
เฉพาะ ตัวแปรโลคัลเท่านั้นที่ถูกเก็บไว้ในเธรดสแต็ก
ตัวแปรท้องถิ่นที่primitive type
(เช่น int, long ... ) จะถูกเก็บไว้ในthread stack
และเป็นผลให้เธรดอื่นไม่มีการเข้าถึง
ตัวแปรท้องถิ่นที่reference type
(ตัวต่อของObject
) ประกอบด้วยจาก 2 ส่วน - ที่อยู่ (ซึ่งถูกเก็บไว้บนthread stack
) และวัตถุ (ซึ่งถูกเก็บไว้บนheap
)
class MyRunnable implements Runnable() {
public void run() {
method1();
}
void method1() {
int intPrimitive = 1;
method2();
}
void method2() {
MyObject1 myObject1 = new MyObject1();
}
}
class MyObject1 {
MyObject2 myObject2 = new MyObject2();
}
class MyObject2 {
MyObject3 myObject3 = MyObject3.shared;
}
class MyObject3 {
static MyObject3 shared = new MyObject3();
boolean b = false;
}