Senkronize edilmiş statik yöntemler Java'da nasıl çalışır ve Hazırda Bekletme varlıklarını yüklemek için kullanabilir miyim?


179

Temel veri erişimini gerçekleştirmek için Hazırda Beklet işlevlerini çağıracak statik yöntemlere sahip bir util sınıfım varsa. synchronizedİplik güvenliğini sağlamak için yöntemi yapmanın doğru yaklaşım olup olmadığını merak ediyorum .

Bu bilgi aynı DB örneğine erişimini önlemek istiyorum. Ancak, şimdi aşağıdaki kod getObjectByIdbelirli bir sınıf tarafından çağrıldığında tüm sınıflar için çağrılmasını engelliyor eminim .

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
}

Yanıtlar:


136

Statik yöntem kilidinde senkronize edilmiş kullanarak sınıf yöntemlerini ve niteliklerini (örnek yöntemlerinin ve niteliklerinin aksine) senkronize edersiniz

Yani varsayımınız doğru.

Yöntemin senkronize edilmesinin iplik güvenliğini sağlamak için doğru yaklaşım olup olmadığını merak ediyorum.

Pek sayılmaz. Bunun yerine RDBMS'nizin bunu yapmasına izin vermelisiniz. Bu tür şeylerde iyidirler.

Veritabanına erişimi senkronize ederek elde edeceğiniz tek şey, uygulamanızı çok yavaş hale getirmektir. Dahası, gönderdiğiniz kodda her seferinde bir Oturum Fabrikası inşa edersiniz, bu şekilde uygulamanız DB'ye erişmek için gerçek işi yapmaktan daha fazla zaman harcayacaktır.

Aşağıdaki senaryoyu düşünün:

Müşteri A ve B, T tablosunun X kaydına farklı bilgiler girmeye çalışır.

Yaklaşımınızla elde ettiğiniz tek şey, DB'de yine de ne zaman olacağından birinin birbiri ardına çağrıldığından emin olmaktır, çünkü RDBMS aynı anda A'dan yarım ve B'den yarım bilgi eklemelerini önleyecektir. . Sonuç aynı olacak, ancak sadece 5 kat (veya daha fazla) daha yavaş olacaktır.

Hazırda Bekleme belgelerindeki "İşlemler ve Eşzamanlılık" bölümüne göz atmak daha iyi olabilir . Çoğu zaman çözmeye çalıştığınız sorunlar zaten çözüldü ve çok daha iyi bir yol.


1
Çok yardımcı cevap! TEŞEKKÜRLER! Böylece Hibernate, "iyimser kilitleme" ile cnocurrency ile ilgilenir. Sonra herhangi bir veri erişim aynı anda çözmek için "senkronize" yöntemleri kullanmaya gerek yok ?? Veri senkronize değil sadece "senkronize" yöntemleri kullanın ?? .. ne zaman kullanırsın ??
domates

1
1) Bence kötümser kilitleme kullanmanın bazı yolları vardır. 2) Hayır, RDBMS bu işi yapabilir. 3) Verilere aynı anda birden çok iş parçacığı tarafından erişilirse. 4) iki iş parçacığı veri paylaşmak zorunda olduğunda senkronizasyon yararlıdır. İhtiyaçları yoksa, o zaman çok daha iyi!
OscarRyz

7
Herhangi bir fast food restoranı multithread kullanır. Bir iş parçacığı siparişinizi alır ve başka bir iş parçacığını hazırlamak için kullanır ve bir sonraki müşteriyle devam eder. Senkronizasyon noktası yalnızca neyin hazırlanacağını bilmek için bilgi alışverişinde bulunduklarında çalışır. Böyle bir modeli takip etmek hayatı gerçekten basitleştirir.
OscarRyz

5
"tüm sınıf" kilitli değil . Java makine dili spesifikasyonu : 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.bir iş parçacığı statik yöntemi girmesi Böylece eğer, aynı nesne tarafından döndürülen nesne # getClass kilitlenir. Diğer evreler yine de örnek yöntemlere erişebilir.
Martin Andersson

4
lol ben kendi ifadeler de sonunda doğru olmadığını buluyorum. Dedim ki "Böylece bir iş parçacığı statik bir yönteme girerse, Object # getClass tarafından döndürülen aynı nesne kilitlenir". Teknik olarak doğru değil. Uzun hikaye tüm meraklı insanlar için kısaldı: Uygulamanızdaki her sınıf için Class, sanal makine sınıf yükleyicilerinden biri tarafından başlatılan bir nesne var . Tüm nesneler gibi, bu nesnenin de onunla bir Monitorilişkisi vardır. Ve bu monitör kilitleniyor.
Martin Andersson

233

Soruyu daha genel olarak ele almak için ...

Senkronize yöntemlerde kullanmanın gerçekten sadece kestirme olduğunu unutmayın (sınıfın SomeClass olduğunu varsayalım):

synchronized static void foo() {
    ...
}

aynıdır

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

ve

synchronized void foo() {
    ...
}

aynıdır

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

Herhangi bir nesneyi kilit olarak kullanabilirsiniz. Statik yöntemlerin alt kümelerini kilitlemek istiyorsanız,

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) {...}
    }
}

(statik olmayan yöntemler için, kilitlerin statik olmayan alanlar olmasını istersiniz)


9
Bu ilk 4 kod bloğu altındır. Tam aradığım şey. Teşekkür ederim.
Ryan Shillington

Statik olmayan yöntemde statik bir Kilit kullanırsam, SomeClass sınıfının iki nesnesinin bloğu aynı anda çalıştıramayacağı doğru mu?
Samuel

2
@Samuel - Neredeyse ... Nesnelerden daha çok iş parçacıkları hakkında. SomeClass'ın ayrı örneklerinin hepsinin aynı kilidi / monitörü kullanacağından haklısınız: Someclass.class nesnesiyle ilişkili olan. Dolayısıyla, iki farklı iş parçacığı iki farklı SomeClass örneğini işliyorsa, ikisi de aynı anda çalışamazdı. Ancak, SomeClass öğesinin bir örneğinde yöntem olarak adlandırılan tek bir iş parçacığı ve diğer örnekte yöntem olarak adlandırılan bu yöntemde herhangi bir engelleme olmaz.
Scott Stanchfield

@ScottStanchfield Senkronizasyon yöntemleri için yollar listelediniz, hepsi eşdeğer mi?
Bionix1441

1
@ Bionix1441 - Her şey kapsam belirleme ile ilgili. Yukarıdaki her mekanizma size kilitleme üzerinde daha iyi kontrol sağlar. İlk olarak, örneğin kendisi bir yöntemin tamamını kilitleyen örneğin kendisini bir yöntemin içindeki bölümleri kilitlemek için, daha sonra bölümleri kilitlemek için herhangi bir nesne örneği kullanarak.
Scott Stanchfield

17

Statik yöntemler sınıfı, örneğin nesne için Utils.class olan kilitleme için nesne olarak kullanır. Yani evet, sorun değil.


14

static synchronizedsınıfın Classnesnesinde kilit tutmak anlamına gelir . synchronized kilit tutma anlamına gelir sınıfın nesnesinin kendisine kilit tutma anlamına gelir. Bu, bir iş parçacığında (yürütme) statik olmayan bir eşitlenmiş yönteme erişiyorsanız, başka bir iş parçacığı kullanarak statik bir eşitlenmiş yönteme erişmeye devam edebileceğiniz anlamına gelir.

Bu nedenle, aynı tür iki yönteme (iki statik veya statik olmayan iki yöntem) herhangi bir zamanda bir iş parçacığından daha fazla erişmek mümkün değildir.


10

Neden DB'ye aynı anda yalnızca tek bir iş parçacığının erişebileceğini zorlamak istiyorsunuz?

Veritabanı sürücüsünün görevi,ConnectionBir kerede yalnızca bir iş parçacığı tarafından kullanıldığı !

Büyük olasılıkla, veritabanınız çoklu, paralel erişimi mükemmel şekilde yönetebilir


Bahse girerim, bazı işlemsel sorunlar için bir çözümdür. Yani, çözüm gerçek sorunu çözmüyor
matt b

1
Bunu bilmiyordum .... Bunu manuel olarak uygulamak zorunda kalacağımı düşündüm. Gösterdiğiniz için teşekkürler! :)
domates

2

Veritabanınızdaki verilerle ilgili bir şey varsa, neden ulaşmak için veritabanı yalıtım kilidini kullanmıyorsunuz?


Veritabanı arka planım yok. Şimdi biliyorum!! Gösterdiğiniz için teşekkürler! :)
domates

2

Sorunuzu cevaplamak için evet öyle: synchronizedyönteminiz aynı anda birden fazla iş parçacığı tarafından yürütülemez.


2

synchronizedJava anahtar kelimesi nasıl çalışır?

synchronizedAnahtar kelimeyi statik bir yönteme eklediğinizde, yöntem aynı anda yalnızca tek bir iş parçacığı tarafından çağrılabilir.

Sizin durumunuzda, her yöntem çağrısı:

  • yeni bir tane oluştur SessionFactory
  • yeni bir tane oluştur Session
  • varlığı getir
  • varlığı arayana geri döndürme

Ancak, bunlar gereksinimlerinizdi:

  • Bu bilgi aynı DB örneği için erişimi önlemek istiyorum.
  • getObjectByIdbelirli bir sınıf tarafından çağrıldığında tüm sınıflar için çağrılmayı önleme

Bu nedenle, getObjectByIdyöntem iş parçacığı için güvenli olsa bile uygulama yanlıştır.

SessionFactory en iyi uygulamalar

İş SessionFactoryparçacığı için güvenlidir ve varlık sınıflarını ayrıştırmak ve iç varlık metamodel temsilini oluşturmak gerektiğinden oluşturmak çok pahalı bir nesnedir.

Bu nedenle, SessionFactoryher getObjectByIdyöntem çağrısında on oluşturmamalısınız .

Bunun yerine, bunun için tek bir örnek oluşturmalısınız.

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

Her Sessionzaman kapalı olmalı

SessionBir finallyblokta kapatmadınız ve varlık yüklenirken bir istisna atılırsa veritabanı kaynaklarına sızabilir.

Yönteme göre Session.loadJavaDocHibernateException varlık veritabanında bulunamazsa a atabilir .

Bir örneğin olup olmadığını belirlemek için bu yöntemi kullanmamalısınız ( get()bunun yerine kullanın). Bunu yalnızca, var olmamanın gerçek bir hata olacağı varsayıldığınız bir örneği almak için kullanın.

Bu nedenle finally, kapatmak için bir blok kullanmanız gerekir 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(); 
         }
     }
 }

Çok iş parçacıklı erişimi önleme

Sizin durumunuzda, belirli bir varlığa yalnızca bir iş parçacığının erişmesini sağlamak istediniz.

Ancak synchronizedanahtar kelime yalnızca iki iş parçacığının getObjectByIdaynı anda çağrılmasını önler . İki iş parçacığı bu yöntemi birbiri ardına çağırırsa, yine de bu varlığı kullanan iki iş parçacığınız olur.

Bu nedenle, belirli bir veritabanı nesnesini başka bir iş parçacığının değiştiremeyeceği şekilde kilitlemek istiyorsanız, veritabanı kilitlerini kullanmanız gerekir.

synchronizedAnahtar kelime tek bir JVM çalışır. Birden çok web düğümünüz varsa, bu, birden çok JVM'de çok iş parçacıklı erişimi engellemez.

Yapmanız gereken şey, değişiklikleri DB'ye uygularken LockModeType.PESSIMISTIC_READveyaLockModeType.PESSIMISTIC_WRITE şu şekilde uygulamanızdır:

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(); 
    }
}

Ben de öyle yaptım:

  • EntityTransactionYeni bir veritabanı oluşturdum ve yeni bir veritabanı işlemi başlattım
  • Ben yüklenen Postilişkili veritabanı kayıt üzerinde kilit tutarken varlık
  • Varlığı değiştirdim Postve işlemi taahhüt ettim
  • ExceptionAtılan bir durumda , işlemi geri aldım

ACID ve veritabanı işlemleri hakkında daha fazla bilgi için bu makaleye de göz atın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.