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


401

ใครสามารถบอกข้อดีของวิธีการซิงโครไนซ์กับบล็อกซิงโครไนซ์กับตัวอย่างได้หรือไม่?



1
สำเนาซ้ำกันของ: stackoverflow.com/questions/442564/…และstackoverflow.com/questions/416183/…
Yuval Adam

1
@cletus คำถามนี้แตกต่างอย่างสิ้นเชิงจากstackoverflow.com/questions/442564/…
Yukio Fukuzawa

คำตอบ:


431

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

ไม่มีข้อได้เปรียบที่ชัดเจนของการใช้วิธีการซิงโครไนซ์กับบล็อก

บางทีอาจจะเป็นเพียงคนเดียว ( แต่ผมจะไม่เรียกว่าการพิจารณาเป็นพิเศษ) thisคือคุณไม่จำเป็นต้องมีการอ้างอิงวัตถุ

วิธี:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

บล็อก:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

ดู? ไม่มีข้อได้เปรียบเลย

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

เปรียบเทียบ:

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

เมื่อเทียบกับ

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

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

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}

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

59
ฉันรู้ว่านี่เป็นคำถามเก่า แต่การซิงโครไนซ์กับ "สิ่งนี้" ได้รับการพิจารณาในบางวงการว่าเป็นรูปแบบต่อต้าน ผลลัพธ์ที่ไม่ได้ตั้งใจคือบุคคลภายนอกคลาสสามารถล็อคการอ้างอิงวัตถุที่เท่ากับ "นี่" และป้องกันไม่ให้เธรดอื่นผ่านอุปสรรคภายในคลาสที่อาจสร้างสถานการณ์การหยุดชะงัก การสร้าง "private final Object = new Object ();" ตัวแปรล้วนๆสำหรับวัตถุประสงค์ในการล็อคเป็นโซลูชั่นที่ใช้บ่อย นี่คือคำถามอื่นที่เกี่ยวข้องโดยตรงกับปัญหานี้
justin.hughey

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

4
สิ่งที่น่าสนใจเกี่ยวกับเรื่องนี้คือการใช้วิธีการซิงโครไนซ์จะทำให้โค้ดที่สร้างขึ้นมีคำสั่งน้อยลง 1 อันเนื่องจากเมธอดมีบิตซิงโครไนซ์ที่ถูกทำให้เป็นลายเซ็น เนื่องจากความยาวของไบต์เป็นปัจจัยในการที่วิธีการเรียงกันการย้ายบล็อกไปยังลายเซ็นวิธีอาจแตกต่างในการตัดสินใจ ในทางทฤษฎีแล้วล่ะ ฉันจะไม่ตัดสินใจเกี่ยวกับการออกแบบตามคำสั่งไบต์เดียวที่บันทึกไว้ซึ่งดูเหมือนว่าเป็นความคิดที่แย่มาก แต่ก็ยังเป็นความแตกต่าง =)
corsiKa

2
@corsiKa: คุณประหยัดได้มากกว่าหนึ่งคำสั่ง synchronizedบล็อกจะดำเนินการโดยใช้สองคำแนะนำmonitorenterและmonitorexit, บวกการจัดการข้อยกเว้นซึ่งมั่นใจว่าmonitorexitจะเรียกว่าแม้ในกรณีพิเศษ นั่นคือทั้งหมดที่บันทึกไว้เมื่อใช้synchronizedวิธีการ
Holger

139

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

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

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


หลังยังสามารถทำบุญได้หากไม่จำเป็นต้องซิงโครไนซ์รหัสทั้งหมดใน foo ()
Evan

1
นี่เป็นเรื่องจริง แต่ไม่ใช่สิ่งที่ "นักรบ" ถาม: "ข้อดีของวิธีการซิงโครไนซ์" ไม่มี
OscarRyz

76

วิธีการซิงโครไนซ์

ข้อดี:

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

จุดด้อย:

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

บล็อกซิงโครไนซ์

ข้อดี:

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

จุดด้อย:

  • ไวยากรณ์มีความซับซ้อนมากขึ้นและทำให้อ่านรหัสได้ยากขึ้น

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


เมื่อคุณพูดว่า "อยู่ในชั้นเรียน" คุณหมายถึง "อยู่ในวัตถุ " หรือฉันขาดอะไรไปหรือเปล่า
OldPeculier

36

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

สมมติว่าคุณมีคิวข้อความและผู้ผลิตข้อความและผู้บริโภคหลายคน เราไม่ต้องการให้ผู้ผลิตรบกวนซึ่งกันและกัน แต่ผู้บริโภคควรสามารถดึงข้อความได้โดยไม่ต้องรอผู้ผลิต ดังนั้นเราแค่สร้างวัตถุ

Object writeLock = new Object();

และต่อจากนี้ไปทุกครั้งที่ผู้ผลิตต้องการเพิ่มข้อความใหม่เราเพิ่งล็อคข้อความนั้น:

synchronized(writeLock){
  // do something
}

ดังนั้นผู้บริโภคอาจยังอ่านและผู้ผลิตจะถูกล็อค


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

30

วิธีการซิงโครไนซ์

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

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

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

งบประสาน

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

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

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

package test;

public class SynchTest implements Runnable {  
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

ตรวจสอบผลลัพธ์ที่ต่างกันด้วยวิธีการซิงโครไนซ์บล็อกและไม่มีการซิงโครไนซ์


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

ฉันทดสอบโค้ดของคุณแบบกำกับแล้ว แต่ C นั้นเป็น 0 เสมอแล้วคือ -2024260031 และสิ่งเดียวที่เปลี่ยนรหัสแฮช สิ่งที่พฤติกรรมที่ควรจะเห็น?
Justin Johnson

คุณควรอ้างอิงบทความด้านล่างซึ่งมีการให้เนื้อหา: docs.oracle.com/javase/tutorial/essential/concurrency/… และdocs.oracle.com/javase/tutorial/essential/concurrency/
Ravindra babu

29

หมายเหตุ: วิธีการแบบซิงโครไนซ์แบบคงที่และบล็อกทำงานบนวัตถุคลาส

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}

18

เมื่อคอมไพเลอร์ java แปลงซอร์สโค้ดของคุณเป็นโค้ดไบต์มันจะจัดการวิธีการซิงโครไนซ์และบล็อกที่ซิงโครไนซ์แตกต่างกันมาก

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

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

สิ่งนี้แสดงให้เห็นการเรียกเพื่อสร้างทั้งวิธีการซิงโครไนซ์และบล็อกซิงโครไนซ์:

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

synchronizedMethodGet()วิธีการสร้างรหัสไบต์ต่อไปนี้:

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

และนี่คือรหัสไบต์จากsynchronizedBlockGet()วิธีการ:

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

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

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


1
ถ้าเราดูวิธีการซิงโครไนซ์ bytecode bytecode นั้นกะทัดรัดและเรียบง่ายกว่าดังนั้นทำไมมันไม่เร็วกว่านั้นซิงโครไนซ์บล็อก
eatSleepCode

@eatSleepCode โปรดทราบว่านี่คือ bytecode ซึ่ง "รวบรวม" เพิ่มเติมโดย JVM JVM จะเพิ่มในสิ่งที่จำเป็นmonitorenterและmonitorexitก่อนที่จะเรียกใช้รหัส
Philip Couling

12

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

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

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

public void put( String s, Bar b ) {
  synchronized( myMap ) {
    myMap.put( s,b );
    // then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public void hasKey( String s, ) {
  synchronized( myMap ) {
    myMap.hasKey( s );
  }
}

public void add( Foo f ) {
  synchronized( myList ) {
    myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public Thing getMedianFoo() {
  Foo med = null;
  synchronized( myList ) {
    Collections.sort(myList);
    med = myList.get(myList.size()/2); 
  }
  return med;
}

7

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


6

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

ตัวอย่างต่อไปนี้พิมพ์วิธีการซิงโครไนซ์ทั้งหมดของ Hashtable:

for (Method m : Hashtable.class.getMethods()) {
        if (Modifier.isSynchronized(m.getModifiers())) {
            System.out.println(m);
        }
}

5

หมายเหตุสำคัญเกี่ยวกับการใช้บล็อกที่ซิงโครไนซ์: ระวังสิ่งที่คุณใช้เป็นวัตถุล็อค!

ข้อมูลโค้ดจากผู้ใช้ 2277816 ด้านบนแสดงจุดนี้ว่าการอ้างอิงถึงตัวอักษรสตริงถูกใช้เป็นวัตถุล็อค ตระหนักดีว่าตัวอักษรสตริงจะถูก interned โดยอัตโนมัติใน Java และคุณควรเริ่มเห็นปัญหา: ทุกส่วนของรหัสที่ประสานกับ "ล็อค" ตัวอักษรแบ่งปันล็อคเดียวกัน! สิ่งนี้สามารถนำไปสู่การหยุดชะงักได้อย่างง่ายดายด้วยโค้ดที่ไม่เกี่ยวข้องอย่างสมบูรณ์

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

สำหรับข้อมูลเพิ่มเติมโปรดดูที่: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused


5

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

นี่คือตัวอย่าง

ระดับวิธีการ

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

ระดับบล็อก

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

[แก้ไข]

สำหรับCollectionไลค์VectorและHashtableพวกมันจะซิงโครไนซ์เมื่อArrayListหรือHashMapไม่และคุณต้องตั้งค่าคีย์เวิร์ดที่ซิงโครไนซ์หรือเรียกใช้เมธอด Collections ที่ซิงโครไนซ์:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list

5

ความแตกต่างเพียงอย่างเดียว: บล็อกที่ซิงโครไนซ์อนุญาตให้ล็อกแบบละเอียดซึ่งต่างจากวิธีการซิงโครไนซ์

โดยทั่วไปsynchronizedบล็อกหรือวิธีการถูกใช้เพื่อเขียนรหัสปลอดภัยของเธรดโดยหลีกเลี่ยงข้อผิดพลาดที่ไม่สอดคล้องกันของหน่วยความจำ

คำถามนี้เก่ามากและมีการเปลี่ยนแปลงหลายอย่างในช่วง 7 ปีที่ผ่านมา โครงสร้างการเขียนโปรแกรมใหม่ได้รับการแนะนำเพื่อความปลอดภัยของเธรด

คุณสามารถบรรลุความปลอดภัยของเธรดโดยใช้ API การทำงานพร้อมกันขั้นสูงแทนsynchroniedบล็อก หน้าเอกสารนี้ให้โครงสร้างการเขียนโปรแกรมที่ดีเพื่อความปลอดภัยของเธรด

Lock Objectsรองรับการล็อกสำนวนที่ทำให้แอพพลิเคชั่นหลาย ๆ

ผู้บริหารกำหนด API ระดับสูงสำหรับการเรียกใช้และการจัดการเธรด การปรับใช้ Executor ที่จัดทำโดย java.util.concurrent จัดเตรียมการจัดการเธรดพูลที่เหมาะสำหรับแอ็พพลิเคชันขนาดใหญ่

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

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

ThreadLocalRandom (ใน JDK 7) ให้การสร้างหมายเลข pseudorandom ที่มีประสิทธิภาพจากหลายเธรด

การแทนที่ที่ดีกว่าสำหรับการซิงโครไนซ์คือReentrantLockซึ่งใช้LockAPI

Reentrant mutual exclusion Lock ที่มีพฤติกรรมพื้นฐานและความหมายเหมือนกับการล็อกการมอนิเตอร์โดยนัยที่เข้าถึงได้โดยใช้วิธีการและข้อความที่ซิงโครไนซ์ แต่มีความสามารถเพิ่มเติม

ตัวอย่างที่มีล็อค:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

อ้างถึงjava.util.concurrentและjava.util.concurrent.atomicแพ็คเกจเกินไปสำหรับการเขียนโปรแกรมการสร้างอื่น ๆ

อ้างถึงคำถามที่เกี่ยวข้องเช่นกัน:

การประสานเทียบกับล็อค


4

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


3

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


2

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

คำอธิบายโดยละเอียดเพิ่มเติมเกี่ยวกับความแตกต่างที่คุณสามารถพบได้ที่นี่: http://www.artima.com/insidejvm/ed2/threadsynchP.html


2

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

ตัวอย่าง:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }

2

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ด้วยการอ่านคำตอบอย่างรวดเร็วของฉันที่นี่ฉันไม่เห็นใครพูดถึงว่าบางครั้งsynchronizedวิธีการอาจล็อคผิด
จาก Java Concurrency In ปฏิบัติ (หน้า 72):

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

public syncrhonized boolean putIfAbsent(E x) {
 boolean absent = !list.contains(x);
if(absent) {
 list.add(x);
}
return absent;
}

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

public boolean putIfAbsent(E x) {
 synchronized(list) {
  boolean absent = !list.contains(x);
  if(absent) {
    list.add(x);
  }
  return absent;
}
}

รหัสด้านบนจะบล็อกเธรดทั้งหมดที่พยายามแก้ไขรายการจากการแก้ไขรายการจนกว่าบล็อกที่ซิงโครไนซ์จะเสร็จสมบูรณ์


การอ่านหนังสือเล่มนี้ในขณะนี้ ... ฉันสงสัยว่า ... ถ้ารายการนั้นเป็นแบบส่วนตัวแทนที่จะเป็นแบบสาธารณะและมีวิธีการใส่ ifAbsent การซิงโครไนซ์ (นี่) จะเพียงพอใช่ไหม ปัญหาในมือเป็นเพราะรายการสามารถแก้ไขนอก ListHelper นั้นด้วย?
dtc

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

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

2

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

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

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


1

จากสรุปข้อมูลจำเพาะ Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

คำสั่งที่ทำข้อมูลให้ตรงกัน (§14.17) คำนวณการอ้างอิงไปยังวัตถุ จากนั้นจะพยายามทำการล็อคการทำงานกับวัตถุนั้นและจะไม่ดำเนินการต่อไปจนกว่าการดำเนินการล็อคจะเสร็จสมบูรณ์ ...

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

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

แก้ไข: ฉันเดิมคิดว่าสิ่งเหล่านี้เป็นคำพูดของสเป็ค Java ที่แท้จริง ชี้แจงว่าหน้านี้เป็นเพียงบทสรุป / คำอธิบายของข้อมูลจำเพาะ


1

TLDR; ค่าใช้synchronizedปรับปรุงหรือsynchronized(this){...}การแสดงออก แต่synchronized(myLock){...}ที่myLockเป็นเช่นสนามสุดท้ายถือเป็นวัตถุส่วนตัว


ความแตกต่างระหว่างการใช้โมดิsynchronizedฟายเออร์ในการประกาศเมธอดและsynchronized(..){ }นิพจน์ในส่วนของเมธอดคือ:

  • โมดิsynchronizedฟายเออร์ที่ระบุบนลายเซ็นของเมธอด
    1. สามารถมองเห็นได้ใน JavaDoc ที่สร้างขึ้น
    2. เป็นโปรแกรมที่ระบุได้ผ่านการสะท้อนเมื่อการทดสอบปรับปรุงวิธีการของสำหรับModifier.SYNCHRONIZED ,
    3. ต้องใช้การพิมพ์และการเยื้องน้อยกว่าเมื่อเทียบกับsynchronized(this) { .... }และ
    4. (ขึ้นอยู่กับ IDE ของคุณ) สามารถมองเห็นได้ในโครงร่างของชั้นเรียนและการเติมโค้ดให้สมบูรณ์
    5. ใช้thisวัตถุเป็นล็อคเมื่อมีการประกาศในวิธีการไม่คงที่หรือชั้นล้อมรอบเมื่อมีการประกาศในวิธีการคงที่
  • การsynchronized(...){...}แสดงออกช่วยให้คุณ
    1. เพื่อซิงโครไนซ์การเรียกใช้งานส่วนต่าง ๆ ของเนื้อหาของวิธี
    2. ที่จะใช้ภายในตัวสร้างหรือบล็อกเริ่มต้น( คงที่ )
    3. เพื่อเลือกวัตถุล็อคที่ควบคุมการเข้าถึงข้อมูลให้ตรงกัน

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

ดังนั้นวิธีปฏิบัติที่ดีที่สุดคือไม่ใช้synchronizedตัวดัดแปลงหรือsynchronized(...)นิพจน์ร่วมกับthisวัตถุล็อค แต่วัตถุล็อคส่วนตัวกับวัตถุนี้ ตัวอย่างเช่น:

public class MyService {
    private final lock = new Object();

    public void doThis() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }

    public void doThat() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }
}

คุณยังสามารถใช้วัตถุล็อคหลาย ๆ อันได้ แต่ต้องมีการดูแลเป็นพิเศษเพื่อให้แน่ใจว่าสิ่งนี้จะไม่ส่งผลให้เกิดการหยุดชะงักเมื่อใช้งานซ้อนกัน

public class MyService {
    private final lock1 = new Object();
    private final lock2 = new Object();

    public void doThis() {
       synchronized(lock1) {
          synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThat() and doMore().
          }
    }

    public void doThat() {
       synchronized(lock1) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doMore() may execute concurrently
        }
    }

    public void doMore() {
       synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doThat() may execute concurrently
        }
    }
}

1

ฉันคิดว่าคำถามนี้เป็นเรื่องเกี่ยวกับความแตกต่างระหว่างการเริ่มต้นThread Safe SingletonและLazy ด้วยการล็อคการตรวจสอบซ้ำ ฉันมักจะอ้างถึงบทความนี้เมื่อฉันจำเป็นต้องใช้ซิงเกิลที่เฉพาะเจาะจง

ทีนี้นี่คือเธรด Safe Singleton :

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

ข้อดี:

  1. การเริ่มต้น Lazy เป็นไปได้

  2. มันเป็นหัวข้อที่ปลอดภัย

จุดด้อย:

  1. เมธอด getInstance () ถูกซิงโครไนซ์ดังนั้นจึงทำให้ประสิทธิภาพการทำงานช้าลงเนื่องจากหลายเธรดไม่สามารถเข้าถึงพร้อมกันได้

นี่คือการเริ่มต้น Lazy ด้วยการล็อคการตรวจสอบซ้ำ :

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

ข้อดี:

  1. การเริ่มต้น Lazy เป็นไปได้

  2. นอกจากนี้ยังปลอดภัยด้าย

  3. ประสิทธิภาพลดลงเนื่องจากคำหลักที่ซิงโครไนซ์ถูกเอาชนะ

จุดด้อย:

  1. ครั้งแรกอาจส่งผลต่อประสิทธิภาพ

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

โปรดอ้างอิงบทความนี้สำหรับรายละเอียดเพิ่มเติม:

https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/


-3

การซิงโครไนซ์กับเธรด 1) ไม่ใช้ซิงโครไนซ์ (นี่) ในเธรดไม่ทำงาน การซิงโครไนซ์กับ (นี้) ใช้เธรดปัจจุบันเป็นวัตถุเธรดการล็อก เนื่องจากแต่ละเธรดเป็นอิสระจากเธรดอื่น ๆ จึงไม่มีการประสานงานการซิงโครไนซ์ 2) การทดสอบโค้ดแสดงให้เห็นว่าใน Java 1.6 บน Mac การซิงโครไนซ์เมธอดไม่ทำงาน 3) ทำข้อมูลให้ตรงกัน (lockObj) โดยที่ lockObj เป็นวัตถุที่ใช้ร่วมกันทั่วไปของกระทู้ทั้งหมดที่ทำข้อมูลให้ตรงกันจะทำงาน 4) การทำงานของ ReenterantLock.lock () และ. unlock () ดูบทเรียนของจาวาสำหรับสิ่งนี้

รหัสต่อไปนี้แสดงให้เห็นถึงจุดเหล่านี้ นอกจากนี้ยังมี Vector-safe แบบเธรดซึ่งจะถูกแทนที่สำหรับ ArrayList เพื่อแสดงว่าเธรดจำนวนมากที่เพิ่มไปยัง Vector จะไม่สูญเสียข้อมูลใด ๆ ในขณะที่ ArrayList เดียวกันจะสูญเสียข้อมูล 0) รหัสปัจจุบันแสดงการสูญเสียข้อมูลเนื่องจากสภาพการแข่งขัน A) แสดงความคิดเห็นบรรทัดปัจจุบันที่มีข้อความกำกับและยกเลิกการใส่เครื่องหมายบรรทัด A ข้างบนแล้วเรียกใช้เมธอดจะสูญเสียข้อมูล แต่ไม่ควร B) ย้อนกลับขั้นตอน A ยกเลิกการใส่ข้อคิดเห็น B และ // end block} จากนั้นเรียกใช้เพื่อดูผลลัพธ์ที่ไม่มีการสูญเสียข้อมูล C) แสดงความคิดเห็น B, ไม่ใส่ข้อคิดเห็น C รันดูการซิงโครไนซ์ (นี้) สูญเสียข้อมูลตามที่คาดไว้ ไม่มีเวลาที่จะเปลี่ยนแปลงรูปแบบทั้งหมดหวังว่านี่จะช่วยได้ หากการซิงโครไนซ์ (นี้) หรือการซิงโครไนซ์วิธีการทำงานโปรดระบุเวอร์ชันของ Java และ OS ที่คุณทดสอบ ขอบคุณ.

import java.util.*;

/** RaceCondition - Shows that when multiple threads compete for resources 
     thread one may grab the resource expecting to update a particular 
     area but is removed from the CPU before finishing.  Thread one still 
     points to that resource.  Then thread two grabs that resource and 
     completes the update.  Then thread one gets to complete the update, 
     which over writes thread two's work.
     DEMO:  1) Run as is - see missing counts from race condition, Run severa times, values change  
            2) Uncomment "synchronized(countLock){ }" - see counts work
            Synchronized creates a lock on that block of code, no other threads can 
            execute code within a block that another thread has a lock.
        3) Comment ArrayList, unComment Vector - See no loss in collection
            Vectors work like ArrayList, but Vectors are "Thread Safe"
         May use this code as long as attribution to the author remains intact.
     /mf
*/ 

public class RaceCondition {
    private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)

    private String countLock="lock";    // Object use for locking the raceCount
    private int raceCount = 0;        // simple add 1 to this counter
    private int MAX = 10000;        // Do this 10,000 times
    private int NUM_THREADS = 100;    // Create 100 threads

    public static void main(String [] args) {
    new RaceCondition();
    }

    public RaceCondition() {
    ArrayList<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class

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