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


179

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

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

public class Utils {
     public static synchronized Object getObjectById (Class objclass, Long id) {
           // call hibernate class
         Session session = new Configuration().configure().buildSessionFactory().openSession();
         Object obj = session.load(objclass, id);
         session.close();
         return obj;
     }

     // other static methods
}

คำตอบ:


136

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

ดังนั้นสมมติฐานของคุณถูกต้อง

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

ไม่ได้จริงๆ คุณควรปล่อยให้ RDBMS ทำงานแทน พวกเขาเก่งเรื่องนี้มาก

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

ลองนึกภาพสถานการณ์ต่อไปนี้:

ไคลเอ็นต์ A และ B พยายามแทรกข้อมูลที่แตกต่างกันในบันทึก X ของตาราง T

ด้วยวิธีการของคุณสิ่งเดียวที่คุณจะได้รับคือการทำให้แน่ใจว่าสิ่งหนึ่งถูกเรียกอีกอย่างหนึ่งเมื่อสิ่งนี้เกิดขึ้นแล้วใน DB เพราะ RDBMS จะป้องกันไม่ให้พวกเขาแทรกข้อมูลครึ่งหนึ่งจาก A และอีกครึ่งหนึ่งจาก B ในเวลาเดียวกัน . ผลลัพธ์จะเหมือนกัน แต่เพียง 5 ครั้ง (หรือมากกว่า) ช้ากว่า

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


1
คำตอบที่เป็นประโยชน์มากขอบคุณ! ดังนั้นไฮเบอร์เนตจะดูแล cnocurrency โดย "การล็อคในแง่ดี" จากนั้นไม่จำเป็นต้องใช้วิธีการ "ทำข้อมูลให้ตรงกัน" เลยเพื่อแก้ไขข้อตกลงการเข้าถึงข้อมูลใด ๆ ? ใช้วิธีการ "ทำข้อมูลให้ตรงกัน" เฉพาะในกรณีที่ข้อมูลไม่ได้ถูกเก็บไว้ในฐานข้อมูลหรือไม่? .. เมื่อไหร่ที่คุณใช้พวกเขา ??
มะเขือเทศ

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

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

5
"คลาสทั้งหมด" ไม่ถูกล็อค Java เปคภาษาเครื่อง : For a class (static) method, the monitor associated with the Class object for the method's class is used. For an instance method, the monitor associated with this (the object for which the method was invoked) is used.ดังนั้นหากด้ายเข้าสู่วิธีการแบบคงที่เดียวกันวัตถุกลับโดยวัตถุ # getClassถูกล็อค เธรดอื่นยังคงสามารถเข้าถึงวิธีการอินสแตนซ์
Martin Andersson

4
ฮ่า ๆ ฉันพบว่าถ้อยคำของฉันเองนั้นไม่ถูกต้องในที่สุด ฉันพูดว่า "ดังนั้นหากหนึ่งเธรดเข้าสู่วิธีการคงที่วัตถุเดียวกันกลับโดย Object # getClass ถูกล็อค" ไม่ถูกต้องทางเทคนิค เรื่องสั้นสั้น ๆ สำหรับทุกคนที่อยากรู้อยากเห็น: สำหรับแต่ละคลาสในแอปพลิเคชันของคุณมีClassวัตถุที่ถูกสร้างอินสแตนซ์โดยหนึ่งใน classloaders ของเครื่องเสมือน เช่นเดียวกับวัตถุทั้งหมดวัตถุนี้ก็มีความMonitorเกี่ยวข้องเช่นกัน และจอภาพนี้เป็นสิ่งที่ถูกล็อค
Martin Andersson

233

เพื่อตอบคำถามโดยทั่วไป ...

โปรดทราบว่าการใช้การซิงโครไนซ์กับวิธีการเป็นเพียงการจดชวเลข (สมมติคลาสเป็น SomeClass):

synchronized static void foo() {
    ...
}

เป็นเช่นเดียวกับ

static void foo() {
    synchronized(SomeClass.class) {
        ...
    }
}

และ

synchronized void foo() {
    ...
}

เป็นเช่นเดียวกับ

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

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

class SomeClass {
    private static final Object LOCK_1 = new Object() {};
    private static final Object LOCK_2 = new Object() {};
    static void foo() {
        synchronized(LOCK_1) {...}
    }
    static void fee() {
        synchronized(LOCK_1) {...}
    }
    static void fie() {
        synchronized(LOCK_2) {...}
    }
    static void fo() {
        synchronized(LOCK_2) {...}
    }
}

(สำหรับวิธีการที่ไม่คงที่คุณจะต้องทำให้การล็อคเป็นฟิลด์ที่ไม่คงที่)


9
บล็อคโค้ด 4 อันดับแรกนั้นเป็นทองคำ สิ่งที่ฉันกำลังมองหา ขอบคุณ.
Ryan Shillington

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

2
@Samuel - เกือบ ... มันเกี่ยวกับเธรดมากกว่าอินสแตนซ์ของวัตถุ คุณถูกต้องแล้วว่าอินสแตนซ์ที่แยกต่างหากของ SomeClass ทั้งหมดจะใช้การล็อก / มอนิเตอร์เดียวกัน: อันที่เกี่ยวข้องกับวัตถุ Someclass.class ดังนั้นหากสองเธรดที่แตกต่างกันกำลังประมวลผลอินสแตนซ์ที่แตกต่างกันสองรายการของ SomeClass ทั้งคู่จะไม่สามารถทำงานในเวลาเดียวกันได้ อย่างไรก็ตามหากเธรดเดี่ยวเรียกว่าเมธอดในอินสแตนซ์หนึ่งของ SomeClass และเมธอดนั้นเรียกว่าเมธอดในอินสแตนซ์อื่นจะไม่มีการบล็อกเกิดขึ้น
Scott Stanchfield

@ScottStanchfield คุณได้แสดงวิธีการซิงโครไนซ์วิธีการเหล่านั้นเทียบเท่ากันทั้งหมดหรือไม่
Bionix1441

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

17

วิธีการคงที่ใช้คลาสเป็นวัตถุสำหรับล็อคซึ่งเป็น Utils.class สำหรับตัวอย่างของคุณ ใช่แล้วมันก็โอเค


14

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

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


10

เหตุใดคุณต้องการบังคับให้มีเธรดเดียวเท่านั้นที่สามารถเข้าถึงฐานข้อมูลได้ตลอดเวลา?

มันเป็นงานของไดรเวอร์ฐานข้อมูลที่จะใช้การล็อคที่จำเป็นConnectionเธรดถูกใช้โดยเธรดครั้งละหนึ่งเท่านั้น!

เป็นไปได้มากว่าฐานข้อมูลของคุณสามารถจัดการการเข้าถึงแบบหลายขนานอย่างสมบูรณ์แบบ


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

1
ฉันไม่รู้ว่า .... ฉันคิดว่าฉันต้องใช้สิ่งนี้ด้วยตนเอง ขอบคุณที่ชี้นำ! :)
มะเขือเทศ

2

หากเป็นสิ่งที่ต้องทำกับข้อมูลในฐานข้อมูลของคุณทำไมไม่ใช้การแยกฐานข้อมูลเพื่อให้บรรลุ?


ฉันไม่มีพื้นหลังฐานข้อมูลใด ๆ ตอนนี้ฉันรู้!! ขอบคุณที่ชี้นำ! :)
มะเขือเทศ

2

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


2

วิธีการsynchronizedทำงานของคีย์เวิร์ด Java

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

ในกรณีของคุณทุกวิธีการโทรจะ:

  • สร้างใหม่ SessionFactory
  • สร้างใหม่ Session
  • ดึงเอนทิตี
  • คืนเอนทิตีกลับไปที่ผู้โทร

อย่างไรก็ตามสิ่งเหล่านี้เป็นข้อกำหนดของคุณ:

  • ฉันต้องการสิ่งนี้เพื่อป้องกันการเข้าถึงข้อมูลไปยังอินสแตนซ์ฐานข้อมูลเดียวกัน
  • ป้องกันgetObjectByIdการถูกเรียกสำหรับคลาสทั้งหมดเมื่อมีการเรียกโดยคลาสเฉพาะ

ดังนั้นแม้ว่าgetObjectByIdเมธอดจะปลอดภัยต่อเธรดการใช้งานนั้นไม่ถูกต้อง

SessionFactory ปฏิบัติที่ดีที่สุด

SessionFactoryเป็นด้ายปลอดภัยและเป็นวัตถุที่มีราคาแพงมากในการสร้างตามความต้องการที่จะแยกชั้นเรียนกิจการและสร้างตัวแทน metamodel นิติบุคคลภายใน

ดังนั้นคุณไม่ควรสร้างการเรียกใช้เมธอดSessionFactoryทุกครั้งgetObjectById

คุณควรสร้างอินสแตนซ์ซิงเกิลแทน

private static final SessionFactory sessionFactory = new Configuration()
    .configure()
    .buildSessionFactory();

Sessionควรถูกปิด

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

ตามSession.loadวิธีการ JavaDocอาจโยน a HibernateExceptionหากไม่พบเอนทิตีในฐานข้อมูล

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

นั่นเป็นเหตุผลที่คุณต้องใช้finallyบล็อกเพื่อปิดSessionเช่นนี้:

public static synchronized Object getObjectById (Class objclass, Long id) {    
     Session session = null;
     try {
         session = sessionFactory.openSession();
         return session.load(objclass, id);
     } finally {
         if(session != null) {
             session.close(); 
         }
     }
 }

การป้องกันการเข้าถึงแบบหลายเธรด

ในกรณีของคุณคุณต้องการให้แน่ใจว่ามีเพียงหนึ่งเธรดเท่านั้นที่สามารถเข้าถึงเอนทิตีนั้น

แต่synchronizedคำหลักป้องกันไม่ให้สองเธรดเรียกgetObjectByIdพร้อมกันเท่านั้น หากทั้งสองเธรดเรียกใช้เมธอดนี้หลังจากที่อื่นคุณจะยังคงมีสองเธรดที่ใช้เอนทิตีนี้

ดังนั้นถ้าคุณต้องการล็อควัตถุฐานข้อมูลที่กำหนดดังนั้นไม่มีเธรดอื่นสามารถแก้ไขได้ดังนั้นคุณต้องใช้การล็อคฐานข้อมูล

synchronizedคำหลักที่ทำงานเฉพาะใน JVM เดียว หากคุณมีหลายเว็บโหนดจะไม่ป้องกันการเข้าถึงหลายเธรดใน JVM หลายรายการ

สิ่งที่คุณต้องทำคือใช้LockModeType.PESSIMISTIC_READหรือLockModeType.PESSIMISTIC_WRITEขณะใช้การเปลี่ยนแปลงกับฐานข้อมูลเช่นนี้:

Session session = null;
EntityTransaction tx = null;

try {
    session = sessionFactory.openSession();

    tx = session.getTransaction();
    tx.begin();

    Post post = session.find(
        Post.class, 
        id, 
        LockModeType.LockModeType.PESSIMISTIC_READ
    );

    post.setTitle("High-Performance Java Perisstence");

    tx.commit();
} catch(Exception e) {
    LOGGER.error("Post entity could not be changed", e);
    if(tx != null) {
        tx.rollback(); 
    }
} finally {
    if(session != null) {
        session.close(); 
    }
}

ดังนั้นนี่คือสิ่งที่ฉันทำ:

  • ฉันสร้างใหม่EntityTransactionและเริ่มทำธุรกรรมฐานข้อมูลใหม่
  • ฉันโหลดPostเอนทิตีในขณะที่ล็อคล็อกในฐานข้อมูลที่เกี่ยวข้อง
  • ฉันเปลี่ยนPostเอนทิตีและยอมรับธุรกรรม
  • ในกรณีที่มีExceptionการโยนฉันย้อนกลับการทำธุรกรรม

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ ACID และฐานข้อมูลธุรกรรมตรวจสอบบทความนี้เช่นกัน

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