본문 바로가기
java

[자바 병렬 프로그래밍] Lock에 대한 궁금점

by 참된오징어 2021. 12. 21.

자바 병렬 프로그래밍이란 책을 읽으면서 다음과 같은 내용이 있다.

 

public class Parent {
	public synchronized void do() {
    	System.out.println("parent");
    }
}

public class Child extends Parent {

	@Override
    public synchronized void do() {
    	System.out.println("child");
        supder.do();
    }
}

> 해당 코드는 암묵적인 락이 재진입 가능하지 않았다면 데드락에 빠졌을 코드

 

해당 코드와 설명을 보니 다음 내용처럼 궁금해졌다.

  1. synchronzied method 상속 여부
  2. 재진입을 지원하지 않는 경우 어떤 상황에서 데드락이 걸리는 것일까?
  3. 재진입이 가능하여 데드락 회피가 가능한데, 어떻게 가능한 것일까?

 

 

 

#  1. Synchronized method 상속 여부


http://gee.cs.oswego.edu/dl/cpj/mechanics.html

 

Java Concurrency Constructs

The timed versions of the wait method, wait(long msecs) and wait(long msecs, int nanosecs), take arguments specifying the desired maximum time to remain in the wait set. They operate in the same way as the untimed version except that if a wait has not been

gee.cs.oswego.edu

공식 문서가 아니지만 stackoverflow나 해당 글에서 다음과 같은 내용을 살펴볼 수 있다.

 

The synchronized keyword is not considered to be part of a method's signature. So the synchronized modifier is not automatically inherited when subclasses override superclass methods, and methods in interfaces cannot be declared as synchronized. Also, constructors cannot be qualified as synchronized (although block synchronization can be used within constructors).

Synchronized instance methods in subclasses employ the same lock as those in their superclasses. But synchronization in an inner class method is independent of its outer class. However, a non-static inner class method can lock its containing class, say OuterClass, via code blocks using:
synchronized(OuterClass.this) { /* body */ }.

요약하면 synchronized 키워드는 메서드 시그니쳐로 간주하지 않기 때문에 부모 클래스 메서드를 재정의할 때 동기화 키워드는 자동으로 상속되지 않는다. 하지만 부모 - 자식 간의 동기화된 메서드를 재정의 했다면 동일한 Lock을 사용한다.

 

즉, 1번에 대한 답은 상속은 되지 않지만 동일한 Lock을 사용한다.

 

 

 

 

#  2. 재진입을 지원하지 않는 경우 어떤 상황에서 데드락이 걸리는 것일까?


내가 생각하기에는 다음과 같다.

 

  1. A, B 두 개의 스레드가 있는 상태에서 A 스레드가 먼저 Child의 do() 메서드에 접근하여 lock을 확보한다. (B 스레드는 대기 상태)
  2. A 스레드가 super.do() 부모의 메서드를 실행, 이때 A 스레드가 소유하고 있던 lock을 반환하고 Parent의 lock을 소유
  3. B 스레드가 Child의 do()를 실행, Child의 lock을 확보
  4. A 스레드가 Parent의 do() 메서드 호출 종료, 다시 Child의 lock을 확보하기 위해 대기 중
  5. B 스레드가 Parent의 do() 메서드 호출 대기, Parent의 lock을 확보하기 위해 대기 중
  6. A 스레드 B 스레드 둘 다 대기 상태에 빠져 데드락 발생

위와 같지 않을까 싶다.

 

 

 

#  3. 재진입이 가능하여 데드락 회피가 가능한데, 어떤 방식으로 인해 가능한 것일까?


 

책에서의 설명은 다음과 같다.

 

재진입성은 암묵적인 락은 특정 스레드가 획득한 락을 다시 자기 자신이 확보하는 것을 말한다.

스레드가 해제된 락을 확보하면 JVM이 락에 대한 소유 스레드를 기록하고 확보 횟수를 증가시키고, 

lock을 소유한 스레드가 synchronized 블록 밖으로 나가 lock을 회수하면 확보 횟수를 감소시켜 0이 되면 락이 해제된다.

 

해당 정보가 맞는 정보인지 좀 찾아본 결과 여러 글에서도 확인할 수 있었다.

 

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter

 

Chapter 6. The Java Virtual Machine Instruction Set

The wide instruction modifies the behavior of another instruction. It takes one of two formats, depending on the instruction being modified. The first form of the wide instruction modifies one of the instructions iload, fload, aload, lload, dload, istore,

docs.oracle.com

공식문서에서도 Description 부분에 entry count가 증감이 되어진다고 설명되어있음을 확인할 수 있다.

 

Description

The objectref must be of type reference.

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

  • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.
  • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.
  • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

 

다른 기사에서도 비슷한 내용을 확인할 수 있다.

 

https://www.infoworld.com/article/2076971/how-the-java-virtual-machine-performs-thread-synchronization.html

 

How the Java virtual machine performs thread synchronization

The last article in an <strong>Under the Hood</strong> series on Java bytecode, this article by Bill Venners covers threads, shared data, locks, and more!

www.infoworld.com

Multiple locks
A single thread is allowed to lock the same object multiple times. For each object, the JVM maintains a count of the number of times the object has been locked. An unlocked object has a count of zero. When a thread acquires the lock for the first time, the count is incremented to one. Each time the thread acquires a lock on the same object, a count is incremented. Each time the thread releases the lock, the count is decremented. When the count reaches zero, the lock is released and made available to other threads.

요약하면 싱글 스레드는 동일한 객체를 여러 번 locking 하는 것을 허용한다고 한다.

JVM은 lock count를 관리하는데, unlock일 경우 카운트는 0이고, 잠금을 획득할 때 카운트는 1로 증가하며, 동일한 객체에 lock을 획득할 때마다 카운트는 증가한다고 하며, 잠금 해제되었을 때 다른 스레드가 사용할 수 있다고 한다.

 

 

한국데이터산업진흥원에서도 비슷한 내용을 찾아볼 수 있다.

 

Object의 Header에는 Lock Counter를 가지고 있는데 Lock을 획득하면 1 증가, 놓으면 1 감소한다. Lock을 소유한 Thread만 가능하다. Thread는 한번 수행에 한 번의 Lock만을 획득하거나 놓을 수 있다. Count가 0일 때 다른 Thread가 Lock을 획득할 수 있고 Thread가 반복해서 Lock을 획득하면 Count가 증가한다. Critical Section은 Object Reference와 연계해 동기화를 수행한다. Thread는 Critical Section의 첫 Instruction을 수행할 때 참조하는 Object에 대해 Lock을 획득해야 한다. Critical Section을 떠날 때 Lock은 자동 Release 되며 명시적인 작업은 불필요하다. JVM을 이용하는 사람들은 단지 Critical Section을 지정해주기만 하면 동기화는 자동으로 된다는 것이다.

 

여기서 궁금했던 점은

  1. JVM의 lock count를 증감하고 있는 부분
  2. Object header에 있는 biased lock 필드가 위에서 말하는 Lock counter 일까?

JVM lock count를 증감하는 로직을 찾아볼 순 없었지만 어떤 변수가 증감되는지 아래 링크를 통해 확인할 수 있었다.

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/2d1552b90e2d/src/share/vm/runtime/biasedLocking.hpp#l31

 

jdk8u/jdk8u/hotspot: 2d1552b90e2d src/share/vm/runtime/biasedLocking.hpp

view src/share/vm/runtime/biasedLocking.hpp @ 8225:2d1552b90e2d Added tag jdk8u152-b02 for changeset 60d621df6c58 author asaha date Mon, 27 Mar 2017 08:21:39 -0700 parents c18cbe5936b8 children line source /* * Copyright (c) 2005, 2010, Oracle and/or its a

hg.openjdk.java.net

 

 

biased lock : 주로 하나의 스레드가 한 Object 를 자주 이용하고 경쟁이 적을때, 해당 Object에 대해 lock을 획득하고

다른 Thread 가 참조한 적이 없을때, 자동으로 Lock 을 획득 가능하다. Java 15 에선 deprecated 되었다.

 

2번에 대한 궁금점은 아직 해소가 되지 않았다.

Obejct Header와 biased lock과 같은 lock의 종류와 lock 메커니즘에 대해 좀 더 공부가 필요해보인다.

댓글