หากฉันซิงโครไนซ์สองวิธีในคลาสเดียวกันพวกเขาสามารถทำงานพร้อมกันได้หรือไม่?


164

หากฉันซิงโครไนซ์สองวิธีในคลาสเดียวกันพวกเขาสามารถทำงานพร้อมกันบนวัตถุเดียวกันได้หรือไม่? ตัวอย่างเช่น:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

ฉันรู้ว่าฉันวิ่งไม่ได้ methodA()สองครั้งบนวัตถุเดียวกันในสองเธรดที่แตกต่างกัน สิ่งเดียวกันmethodB()ค่ะ

แต่ฉันสามารถเรียกใช้methodB()เธรดอื่นขณะที่methodA()ยังทำงานอยู่ได้หรือไม่ (วัตถุเดียวกัน)

คำตอบ:


148

ทั้งสองวิธีล็อคจอภาพเดียวกัน ดังนั้นคุณไม่สามารถดำเนินการพร้อมกันบนวัตถุเดียวกันจากเธรดที่แตกต่างกัน (หนึ่งในสองวิธีจะบล็อกจนกว่าจะเสร็จสิ้นอีก)


1
ฉันได้เพิ่มคำถามนี้ สมมติว่าทั้งสองวิธีเป็นแบบคงที่ในขณะนี้ methodA เรียกว่าใช้ Class ขณะที่ methodB เรียกว่าใช้วัตถุเช่น A.methodA () ใน t1 และ obj.methodB () ใน t2 จะเกิดอะไรขึ้นตอนนี้พวกเขาจะปิดกั้น ????
amod

2
@ amod0017: obj.methodB()เป็นตรงกันA.methodB()เมื่อเป็นmethodB() staticดังนั้นใช่พวกเขาจะปิดกั้น (ในชั้นเรียนไม่ใช่วัตถุตรวจสอบ)
NPE

จะพยายามกลับไปหามัน :)
amod

@NPE ดังนั้นแม้ว่าทั้งสองวิธีจะเป็นแบบสแตติกและ 2 เธรด t1 และ t2 บนวัตถุเดียวกันพยายามเรียกเมธอด A () และ methodB () พร้อมกันจากนั้นจะมีเพียง 1 (พูด t1) เธรดเท่านั้นที่จะทำงานและเธรดอื่น ๆ จะต้องรอจนกว่า ?
sreeprasad

8
โปรดทราบว่าวิธีการคงที่ใช้การล็อค.classวัตถุ class A {static synchronized void m() {} }ดังนั้นถ้าคุณมี และจากนั้นหนึ่งเธรดเรียกnew A().m()ว่าได้รับการล็อกบนnew A()วัตถุ ถ้าเธรดอื่นเรียกA.m()ว่าเข้าสู่วิธีการที่ไม่มีปัญหาเพราะสิ่งที่มันค้นหาคือล็อกบนA.classวัตถุในขณะที่ไม่มีเธรดมีการล็อกชนิดนี้ ดังนั้นแม้ว่าคุณประกาศวิธีการsynchronizedมันจริงมีการเข้าถึงโดยสองหัวข้อที่แตกต่างกันในเวลาเดียวกัน ดังนั้น: อย่าใช้การอ้างอิงวัตถุเพื่อเรียกใช้วิธีการคงที่
Alex Semeniuk

113

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

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

เพื่อให้ทั้งสองวิธีการทำงานพร้อมกันพวกเขาจะต้องใช้ล็อคที่แตกต่างกันเช่นนี้

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

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

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

นี่คือวิธีที่การสอน Javaอธิบายความสัมพันธ์:

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

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

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


ดังนั้นในตัวอย่างนี้ล็อคอยู่ในวัตถุ lockA \ lockB และไม่ได้อยู่ในชั้นเรียน? นี่เป็นตัวอย่างการล็อคระดับชั้นหรือไม่
นิมร็

2
@Nimrod: มันล็อคใน lockA และวัตถุ lockB และไม่ได้อยู่ในตัวอย่างของ A. ไม่มีอะไรที่นี่คือล็อคในชั้นเรียน การล็อคระดับชั้นจะหมายถึงการล็อควัตถุคลาสโดยใช้บางอย่างเช่นstatic synchronizedหรือsynchronized (A.class)
Nathan Hughes

นี่คือลิงค์ไปยังบทช่วยสอน Java อธิบายสิ่งที่ตอบไว้ที่นี่
Alberto de Paola

18

Java Thread ได้รับการล็อคระดับวัตถุเมื่อมันเข้าสู่ตัวอย่างวิธีการทำข้อมูลให้ตรงกัน Java และได้รับล็อคระดับระดับเมื่อมันเข้าสู่คงที่วิธีการทำข้อมูลให้ตรงกัน Java

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


ถ้าฉันมีสองเธรดในสองอินสแตนซ์ที่ต่างกันของคลาสจากนั้นพวกเขาจะสามารถดำเนินการทั้งสองวิธีพร้อมกันเพื่อให้เธรดหนึ่งเรียกวิธีการซิงโครไนส์หนึ่งและอีกวิธีหนึ่งเรียกวิธีการซิงโครไนส์ที่สอง หากความเข้าใจของฉันถูกต้องฉันสามารถใช้private final Object lock = new object();กับการซิงโครไนซ์เพื่ออนุญาตให้เธรดเดียวเท่านั้นสามารถใช้วิธีการใดวิธีการหนึ่งได้หรือไม่ ขอบคุณ
Yug Singh เมื่อ

13

ในกรณีของคุณคุณซิงโครไนซ์สองวิธีในอินสแตนซ์เดียวกันของคลาส ดังนั้นทั้งสองวิธีไม่สามารถทำงานพร้อมกันบนเธรดที่ต่างกันของอินสแตนซ์เดียวกันของคลาส A แต่พวกเขาสามารถทำได้บนอินสแตนซ์คลาส A ที่แตกต่างกัน

class A {
    public synchronized void methodA() {
        //method A
    }
}

เหมือนกับ:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

ถ้าฉันกำหนดล็อคเป็นprivate final Object lock = new Object();และตอนนี้ใช้lockกับซิงโครไนซ์บล็อกในสองวิธีแล้วข้อความของคุณจะเป็นจริงหรือไม่? IMO เนื่องจาก Object เป็นคลาสพาเรนต์ของวัตถุทั้งหมดดังนั้นแม้ว่าเธรดจะอยู่ในอินสแตนซ์ที่แตกต่างกันของคลาสมีเพียงคนเดียวเท่านั้นที่สามารถเข้าถึงรหัสภายในบล็อกที่ซิงโครไนซ์ได้ในแต่ละครั้ง ขอบคุณ
Yug Singh เมื่อ

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

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

7

จากเอกสาร Oracle ลิงค์

ทำให้วิธีการทำข้อมูลให้ตรงกันมีสองผล:

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

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

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

ดูที่หน้าเอกสารนี้เพื่อทำความเข้าใจกับการล็อคภายในและพฤติกรรมการล็อค


6

คิดว่าโค้ดของคุณเป็นรหัสด้านล่าง:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

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

แต่ฉันสามารถเรียกใช้ methodB () ในเธรดที่แตกต่างกันในขณะที่ methodA () ยังคงทำงานอยู่หรือไม่ (วัตถุเดียวกัน)

แน่นอนมันเป็นไปไม่ได้!

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


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

2
พวกเขาจะเพราะพวกเขาเป็นวัตถุที่แตกต่างกัน นั่นคือถ้าคุณต้องการป้องกันคุณสามารถใช้วิธีการคงที่และประสานคลาสหรือใช้วัตถุตัวแปรระดับเป็นล็อคหรือทำให้ชั้นเดียว @Yug Singh
Khosro Makari

4

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


3

ที่สำคัญความคิดตรงกันกับที่ไม่ได้จมอยู่ในได้อย่างง่ายๆก็คือว่ามันจะมีผลเฉพาะในกรณีที่วิธีการที่เรียกว่าบนวัตถุเดียวกันเช่น - จะได้รับอยู่แล้วเน้นในคำตอบและความเห็น -

ด้านล่างโปรแกรมตัวอย่างคือการระบุอย่างชัดเจนเหมือนกัน -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

สังเกตเห็นความแตกต่างในผลลัพธ์ของการอนุญาตการเข้าถึงพร้อมกันตามที่คาดไว้หากมีการเรียกใช้เมธอดบนอินสแตนซ์ของวัตถุที่แตกต่างกัน

Ouput กับnoEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () แสดงความคิดเห็น - ผลลัพธ์ที่อยู่ในการสั่งซื้อวิธีการใน> methodA Out .. methodB ใน> methodB Out Ouput ด้วย * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () * แสดงความคิดเห็น

และ Ouput กับsynchronEffectiveAsMethodsCalledOnSameObject () แสดงความคิดเห็น - เอาท์พุทแสดงการเข้าถึงวิธีการพร้อมกันโดย Thread1 และ Thread0 ในส่วนที่เน้น -

Ouput กับ * synchronEffectiveAsMethodsCalledOnSameObject () * แสดงความคิดเห็น

การเพิ่มจำนวนเธรดจะทำให้สังเกตได้ชัดเจนยิ่งขึ้น


2

ไม่เป็นไปไม่ได้ถ้าเป็นไปได้ทั้งสองวิธีสามารถอัพเดตตัวแปรเดียวกันพร้อมกันซึ่งอาจทำให้ข้อมูลเสียหายได้ง่าย


2

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


1

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


0

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


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