จะซิงโครไนซ์ตัวแปรแบบคงที่ระหว่างเธรดที่รันอินสแตนซ์ของคลาสใน Java ได้อย่างไร


120

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

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

เราสามารถทำการซิงโครไนซ์ด้วยวิธีต่อไปนี้ได้หรือไม่?

public class Test  
{  
   private static int count = 0;  
   private static final Object lock= new Object();    
   public synchronized void foo() 
  {  
      synchronized(lock)
     {  
         count++;  
     }  
  }  
}

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


4
คำตอบทั้งหมดนี้ใช้งานไม่ได้เว้นแต่จะมีการประกาศวัตถุล็อค FINAL!

ดู java.util.concurrent.atomic
AtomicInteger

คำตอบ:


193

มีหลายวิธีในการซิงโครไนซ์การเข้าถึงตัวแปรคงที่

  1. ใช้วิธีการซิงโครไนซ์แบบคงที่ สิ่งนี้ซิงโครไนซ์กับวัตถุคลาส

    public class Test {
        private static int count = 0;
    
        public static synchronized void incrementCount() {
            count++;
        }
    } 
  2. ซิงโครไนซ์บนวัตถุคลาสอย่างชัดเจน

    public class Test {
        private static int count = 0;
    
        public void incrementCount() {
            synchronized (Test.class) {
                count++;
            }
        }
    } 
  3. ซิงโครไนซ์กับวัตถุคงที่อื่น ๆ

    public class Test {
        private static int count = 0;
        private static final Object countLock = new Object();
    
        public void incrementCount() {
            synchronized (countLock) {
                count++;
            }
        }
    } 

วิธีที่ 3 เป็นวิธีที่ดีที่สุดในหลาย ๆ กรณีเนื่องจากวัตถุล็อคไม่ได้ถูกเปิดเผยภายนอกชั้นเรียนของคุณ


1
1. คนแรกไม่จำเป็นต้องมีวัตถุล็อคมันไม่ควรดีที่สุด?
Baiyan Huang

4
2. ประกาศการนับเป็นค่าระเหยก็ใช้ได้เช่นกันเนื่องจากความผันผวนทำให้แน่ใจว่าตัวแปรนั้นซิงโครไนซ์
Baiyan Huang

9
เหตุผล # 3 ที่ดีที่สุดคือบิตสุ่มใด ๆ ที่สามารถซิงโครไนซ์Test.classและอาจทำให้วันของคุณเสีย นอกจากนี้การเริ่มต้นคลาสจะทำงานโดยมีการล็อกคลาสไว้ดังนั้นหากคุณมีตัวเริ่มต้นคลาสที่บ้าคลั่งคุณอาจทำให้ตัวเองปวดหัวได้ volatileไม่ได้ช่วยcount++เพราะมันเป็นลำดับการอ่าน / แก้ไข / เขียน ตามที่ระบุไว้ในคำตอบอื่นjava.util.concurrent.atomic.AtomicIntegerน่าจะเป็นตัวเลือกที่เหมาะสมที่นี่
fadden

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

1
@Ferrybig Test.classไม่มีคุณจะถูกล็อค thisจะล็อคสำหรับตรงกันไม่คงที่วิธีการ
user3237736

64

หากคุณเพียงแค่แชร์ตัวนับให้พิจารณาใช้AtomicIntegerหรือคลาสอื่นที่เหมาะสมจากแพ็คเกจ java.util.concurrent.atomic:

public class Test {

    private final static AtomicInteger count = new AtomicInteger(0); 

    public void foo() {  
        count.incrementAndGet();
    }  
}

3
มีอยู่ใน java 1.5 ไม่ใช่ใน 1.6
Pawel

4

ใช่มันเป็นความจริง

หากคุณสร้างสองอินสแตนซ์ของชั้นเรียนของคุณ

Test t1 = new Test();
Test t2 = new Test();

จากนั้น t1.foo และ t2.foo ทั้งสองจะซิงโครไนซ์บนวัตถุคงที่เดียวกันและบล็อกซึ่งกันและกัน


หนึ่งจะปิดกั้นซึ่งกันและกันไม่ใช่กันและกันในคราวเดียวหากได้รับการดูแล
Jafar Ali

0

คุณสามารถซิงโครไนซ์รหัสของคุณผ่านชั้นเรียน นั่นจะง่ายที่สุด

   public class Test  
    {  
       private static int count = 0;  
       private static final Object lock= new Object();    
       public synchronized void foo() 
      {  
          synchronized(Test.class)
         {  
             count++;  
         }  
      }  
    }

หวังว่าคำตอบนี้จะเป็นประโยชน์


2
สิ่งนี้จะใช้งานได้ แต่ตามที่ @Fadden กล่าวไว้ที่อื่นโปรดระวังว่าเธรดอื่น ๆ อาจซิงโครไนซ์Test.classและส่งผลต่อพฤติกรรมได้เช่นกัน นี่คือเหตุผลที่lockอาจต้องการการซิงโครไนซ์
sbk

สิ่งที่คุณกำลังพูดนั้นถูกต้อง นั่นคือเหตุผลที่ฉันพูดถึงอย่างชัดเจนว่าข้างต้นเป็นแนวทางที่ง่ายที่สุด
Jafar Ali

0

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

public class Test {

    private static int count = 0;
    private static final ReentrantLock reentrantLock = new ReentrantLock(); 
    public void foo() {  
        reentrantLock.lock();
        count = count + 1;
        reentrantLock.unlock();
    }  
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.