Java คงที่วิธีการคงที่: ล็อคในวัตถุหรือชั้นเรียน


148

เอกสาร Java กล่าวว่า:

เป็นไปไม่ได้ที่การเรียกใช้วิธีการซิงโครไนซ์สองครั้งบนวัตถุเดียวกันเพื่อแทรกสอด

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

คำตอบ:


129

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

ใช่. :)


81
โปรดตอบอย่างละเอียดเพื่อให้ทุกคนสามารถเข้าใจได้
Madhu

6
@Madhu หมายความว่าหากคุณมีวิธีการซิงโครไนซ์อย่างน้อย 2 วิธีในคลาสเดียวกันทั้งสองจะไม่สามารถดำเนินการได้ในเวลาเดียวกันแม้ว่าจะมีหลายอินสแตนซ์ของคลาสนั้นก็ตาม การล็อคนั้นเหมือนกับการล็อกบน Object.class สำหรับวิธีการซิงโครไนซ์แต่ละวิธี
สตีเวน

คำตอบนี้ผิด - thisเป็นล็อคที่ได้มาจากวิธีการเช่น - โปรดแก้ไขออสการ์
vemv

1
@vemv คำถามนี้เกี่ยวกับวิธีการเรียนไม่ใช่วิธีการเช่น
OscarRyz

23
@vemv ใช่เข้าใจคำตอบที่คุณต้องอ่านคำถามก่อน
OscarRyz

199

เพียงเพื่อเพิ่มรายละเอียดเล็ก ๆ น้อย ๆ ให้กับคำตอบของออสการ์ (รวบรัด!) ส่วนที่เกี่ยวข้องในข้อกำหนดภาษา Java คือ8.4.3.6, 'วิธีการซิงโครไนซ์' :

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


17
ที่มีประโยชน์ผมกำลังมองหาคำพูดที่ 1
OscarRyz

80

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

class A {
    static synchronized f() {...}
    synchronized g() {...}
}

หลัก:

A a = new A();

หัวข้อที่ 1:

A.f();

หัวข้อที่ 2:

a.g();

f () และ g () จะไม่ถูกซิงโครไนซ์ซึ่งกันและกันและทำให้สามารถทำงานได้พร้อมกันโดยสิ้นเชิง


18
แต่จะเกิดอะไรขึ้นถ้า g () กำลังกลายพันธุ์ตัวแปรคงที่ซึ่ง f () กำลังอ่านอยู่ เราจะทำให้เธรดนั้นปลอดภัยได้อย่างไร เราได้ล็อคอย่างชัดเจนในชั้นเรียนหรือไม่?
baskin

22
ใช่วิธีการไม่คงที่ของคุณจะต้องซิงโครไนซ์กับคลาสอย่างชัดเจน (เช่น, synchronized (MyClass.class) {...}.
jfpoilpret

@jfpoilpret "ทำข้อมูลให้ตรงกัน (MyClass.class) {... }" เทียบเท่ากับวิธีนี้ทำให้การทำข้อมูลให้ตรงกันแบบคงที่ใช่ไหม?
crazymind

15

ยกเว้นว่าคุณใช้ g () ดังต่อไปนี้:

g() {
    synchronized(getClass()) {
        ...
    }
}

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


63
โปรดทราบว่าอาจมีโอกาสที่จะมีข้อบกพร่องบางอย่างที่น่ารังเกียจและน่ารังเกียจที่นี่ จำได้ว่าgetClass()ส่งคืนประเภทรันไทม์ ; ถ้าคุณคลาสย่อยคลาสแล้วคลาสพาเรนต์และคลาสลูกจะซิงโครไนซ์บนการล็อกต่างกัน synchronized(MyClass.class)เป็นวิธีที่จะไปหากคุณต้องการให้แน่ใจว่าทุกกรณีใช้การล็อคเดียวกัน
Cowan

4

ดูที่หน้าเอกสารของ oracle เกี่ยวกับ Intrinsic Locks and Synchronization

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


2

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

เช่น: - เมื่อคุณพยายามที่จะสร้างวัตถุจากระดับลูกค้าที่มีอยู่เช่น

Customer c = new Customer();

โหลด Customer.class เป็น RAM ในขณะนั้น Class.class ในชุดเครื่องมือ JDK สร้างวัตถุที่เรียกว่าวัตถุแม่แบบและโหลด Customer.class ลงในวัตถุแม่แบบนั้นสมาชิกแบบคงที่ของ Customer.class นั้นกลายเป็นคุณลักษณะและวิธีการในวัตถุแม่แบบนั้น

ดังนั้นวิธีการหรือคุณลักษณะแบบคงที่จึงมีวัตถุ


2

ตัวอย่างด้านล่างให้ความชัดเจนมากขึ้นระหว่างคลาสและล็อควัตถุหวังว่าตัวอย่างด้านล่างจะช่วยให้ผู้อื่นเช่นกัน :)

ตัวอย่างเช่นเรามีวิธีการด้านล่างหนึ่งคลาสที่ได้รับและอื่น ๆ ได้รับล็อควัตถุ:

public class MultiThread {

    public static synchronized void staticLock() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

    public synchronized void objLock() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}

ดังนั้นตอนนี้เราสามารถมีสถานการณ์ต่อไปนี้:

  1. เมื่อเธรดที่ใช้ออบเจ็กต์เดียวกันพยายามเข้าถึงobjLock หรือ staticLockวิธีเดียวกันในเวลาเดียวกัน (เช่นเธรดทั้งสองพยายามเข้าถึงวิธีเดียวกัน)

    Thread-0 0
    Thread-0 1
    Thread-0 2
    Thread-0 3
    Thread-0 4
    Thread-1 0
    Thread-1 1
    Thread-1 2
    Thread-1 3
    Thread-1 4
  2. เมื่อเธรดที่ใช้ออบเจ็กต์เดียวกันพยายามเข้าถึงstaticLockและobjLockเมธอดในเวลาเดียวกัน (พยายามเข้าถึงวิธีอื่น ๆ )

    Thread-0 0
    Thread-1 0
    Thread-0 1
    Thread-1 1
    Thread-0 2
    Thread-1 2
    Thread-1 3
    Thread-0 3
    Thread-0 4
    Thread-1 4
  3. เมื่อเธรดที่ใช้ออบเจ็กต์อื่นพยายามเข้าถึงstaticLockวิธีการ

    Thread-0 0
    Thread-0 1
    Thread-0 2
    Thread-0 3
    Thread-0 4
    Thread-1 0
    Thread-1 1
    Thread-1 2
    Thread-1 3
    Thread-1 4
  4. เมื่อเธรดที่ใช้ออบเจ็กต์อื่นพยายามเข้าถึงobjLockวิธีการ

    Thread-0 0
    Thread-1 0
    Thread-0 1
    Thread-1 1
    Thread-0 2
    Thread-1 2
    Thread-1 3
    Thread-0 3
    Thread-0 4
    Thread-1 4

0

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

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