Java에서 동기화 된 정적 메소드는 어떻게 작동하며 Hibernate 엔티티를로드하는 데 사용할 수 있습니까?


179

기본 데이터 액세스를 달성하기 위해 Hibernate 함수를 호출하는 정적 메소드가있는 util 클래스가있는 경우. synchronized스레드 안전성을 보장하기 위해 방법을 만드는 것이 올바른 방법인지 궁금합니다 .

동일한 DB 인스턴스에 대한 정보 액세스를 막기 위해 이것을 원합니다. 그러나 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가 해당 작업을 수행하도록해야합니다. 그들은 이런 종류의 것들을 잘합니다.

데이터베이스에 대한 액세스를 동기화하여 얻을 수있는 유일한 것은 응용 프로그램을 매우 느리게 만드는 것입니다. 또한 게시 한 코드에서 매번 세션 팩토리를 작성하면 응용 프로그램이 실제 작업을 수행하는 것보다 DB에 액세스하는 데 더 많은 시간을 소비합니다.

다음 시나리오를 상상해보십시오.

클라이언트 A와 B는 테이블 T의 레코드 X에 다른 정보를 삽입하려고합니다.

접근 방식을 사용하면 DB에서 어쨌든 발생할 때 RDBMS가 A의 절반 정보와 B의 절반 정보를 동시에 삽입하지 못하게되므로 하나만 호출해야합니다. . 결과는 같지만 5 배 이상 느립니다.

아마도 최대 절전 모드 설명서 의 "트랜잭션 및 동시성" 장을 살펴 보는 것이 좋습니다 . 대부분의 경우 해결하려는 문제는 이미 해결되었으며 훨씬 더 나은 방법입니다.


1
매우 유용한 답변! 감사합니다! 따라서 Hibernate는 "낙관적 잠금"을 통해 동시성을 관리합니다. 그런 다음 데이터 액세스 동시성을 해결하기 위해 "동기화 된"방법을 전혀 사용할 필요가 없습니까 ?? 데이터가 데이터베이스에 저장되지 않은 경우에만 "동기화 된"방법을 사용 하시겠습니까 ?? .. 사용할 때 ??
토마토

1
1) 비관적 잠금을 사용하는 수단이 있다고 생각합니다. 2) 아니, RDBMS가 그 일을 할 수 있습니다. 3) 여러 스레드가 동시에 데이터에 액세스하는 경우. 4) 동기화는 두 스레드가 데이터를 공유해야 할 때 유용합니다. 그들이 필요하지 않으면 훨씬 좋습니다!
OscarRyz

7
패스트 푸드 식당은 멀티 스레드를 사용합니다. 한 스레드는 주문하고 다른 스레드를 사용하여 준비하고 다음 고객에게 계속합니다. 동기화 지점은 정보를 교환하여 준비 할 내용을 알고있는 경우에만 작동합니다. 그런 모델을 따르면 인생이 정말 간단 해집니다.
OscarRyz

5
"전체 클래스"는 잠겨 있지 않습니다 . 자바 기계 언어 사양 : 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
lol 나는 내 자신의 표현이 궁극적으로 맞지 않다는 것을 안다. "따라서 하나의 스레드가 정적 메서드에 들어가면 Object # getClass에서 반환 한 동일한 개체가 잠 깁니다." 기술적으로 정확하지 않습니다. 모든 호기심 많은 사람들에게 짧은 이야기 : 응용 프로그램의 각 클래스마다 Class가상 머신 클래스 로더 중 하나에 의해 인스턴스화 된 객체 가 있습니다 . 모든 객체와 마찬가지로이 객체에도 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-Almost ... 객체 인스턴스보다 스레드에 관한 것입니다. SomeClass의 개별 인스턴스는 모두 동일한 잠금 / 모니터 (Someclass.class 객체와 관련된 잠금 / 모니터)를 사용한다는 것이 맞습니다. 따라서 두 개의 다른 스레드가 SomeClass의 두 개의 다른 인스턴스를 처리하는 경우 둘 다 동시에 실행할 수 없습니다. 그러나 단일 스레드가 SomeClass의 한 인스턴스에서 메소드를 호출하고 해당 메소드가 다른 인스턴스에서 메소드를 호출하면 차단이 발생하지 않습니다.
Scott Stanchfield

@ScottStanchfield 메소드 동기화 방법을 나열했습니다. 모두 동일합니까?
Bionix1441

1
@ Bionix1441-범위에 관한 모든 것입니다. 위의 각 메커니즘을 통해 잠금을보다 세밀하게 제어 할 수 있습니다. 먼저 인스턴스 자체를 사용하여 전체 메서드를 잠근 다음 인스턴스 자체를 사용하여 메서드 내부의 섹션을 잠그고 개체 인스턴스를 사용하여 섹션을 잠급니다.
Scott Stanchfield 18

17

정적 메서드는 클래스를 잠금의 개체로 사용합니다 (예제에서는 Utils.class). 예, 괜찮습니다.


14

static synchronized클래스의 Class객체 를 잠그는 것을 의미 합니다. synchronized 그 클래스의 객체 자체에 잠금을 보유하는 수단. 즉, 실행중인 스레드에서 비 정적 동기화 방법에 액세스하는 경우 다른 스레드를 사용하여 정적 동기화 방법에 계속 액세스 할 수 있습니다.

따라서 스레드보다 많은 시점에서 두 가지 동일한 종류의 메소드 (두 정적 또는 두 개의 비 정적 메소드)에 액세스하는 것은 불가능합니다.


10

단일 스레드 만 한 번에 DB에 액세스 할 수 있도록하는 이유는 무엇입니까?

데이터베이스 드라이버의 작업은 필요한 잠금을 가정하여 필요한 잠금을 구현하는 것입니다.Connection 가 한 번에 하나의 스레드에서만 사용 !

데이터베이스는 여러 개의 병렬 액세스를 완벽하게 처리 할 수 ​​있습니다.


나는 그것이 트랜잭션 문제에 대한 해결책이라고 생각합니다. 즉, 솔루션은 진정한 문제를 해결하지 못합니다
matt b

1
나는 그것을 모른다 .... 나는 이것을 수동으로 구현해야한다고 생각했다. 지적 해 주셔서 감사합니다! :)
tomato

2

데이터베이스의 데이터와 관련이 있다면 데이터베이스 격리 잠금을 사용하여 달성하지 않겠습니까?


데이터베이스 배경이 없습니다. 이제 알아요 !! 지적 해 주셔서 감사합니다! :)
tomato

2

귀하의 질문에 대답하기 위해 그렇습니다 synchronized. 한 번에 두 개 이상의 스레드에서 메소드를 실행할 수 없습니다.


2

어떻게 synchronized자바 키워드 작품

synchronized정적 메소드에 키워드를 추가하면 한 번에 하나의 스레드에서만 메소드를 호출 할 수 있습니다.

귀하의 경우 모든 메소드 호출은 다음을 수행합니다.

  • 새로운 것을 만들다 SessionFactory
  • 새로운 것을 만들다 Session
  • 엔티티를 가져옵니다
  • 엔티티를 다시 발신자에게 반환

그러나 요구 사항은 다음과 같습니다.

  • 동일한 DB 인스턴스에 대한 정보에 액세스하지 못하도록하고 싶습니다.
  • getObjectById특정 클래스에 의해 호출 될 때 모든 클래스에 대한 호출 방지

따라서 getObjectById메서드가 스레드로부터 안전하더라도 구현이 잘못되었습니다.

SessionFactory 모범 사례

SessionFactory스레드 안전이며,이 엔티티 클래스를 구문 분석하고 내부 엔티티 메타 모델 표현을 구축 할 필요가로 만들 수있는 매우 비싼 개체입니다.

따라서 SessionFactory모든 getObjectById메소드 호출 에서 on을 작성해서는 안됩니다 .

대신 싱글 톤 인스턴스를 만들어야합니다.

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

Session항상 닫아야합니다

당신은을 닫지 않은 SessionA의 finally블록 및 개체를로드 할 때 예외가 발생하는 경우이 데이터베이스 리소스를 누출 할 수 있습니다.

Session.load메소드 에 따르면 JavaDocHibernateException데이터베이스에서 엔티티를 찾을 수없는 경우 a를 던질 수 있습니다.

이 메소드를 사용하여 인스턴스가 존재하는지 판별하지 않아야합니다 ( 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에서 멀티 스레드 액세스를 막을 수 없습니다.

당신이해야 할 일은 다음과 같이 변경 사항을 DB에 적용 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.