Tistory View

Android Develop/Java

synchronized : Java 쓰레드 동기화(Synchronization)

God Dangchy What should I do? 2020. 6. 12. 05:32

이미 이 주제로 수많은 글들이 있지만,..... 나도 그냥 한번 정리를 해본다.



일단 동기화라는 말부터 설명을 해야 되는데(대체 누가 이름을 이[따구]로 지어서 헤깔게 하는지), [동기]의 반대말은 [비동기]라는 표현을 쓴다(이름이 이따구니..원..). 워낙 자주 사용되는 표현이라 적응이 되면 어렵지 않지만, 처음 접할 때는 서로 해깔리는 문제다. Synchronization의 뜻은 "조화"다. 따라서 비동기는 "조화롭지 못하다"라는 뜻이 되고, 쓰레드에 적용해 다시 정리하면,



비동기 : 쓰레드가 다른 쓰레드에 신경 쓰지않고 그냥 자기 할일만 한다.

동기    : 쓰레드가 작업이 다른 쓰레드와 조화롭게 동작한다.(다른 쓰레드가 관여할 경우 자신의 실행을 잠시 멈춘다.)


라는 뜻이다.



멀티쓰레드로 프로그래밍을 할 경우, 변수의 값을 변화시키는 동안, 다른 쓰레드에서 이 변수의 값을 변경할 경우 당연히 문제가 발생한다. 간단한 예로 쓰레드A에서 정수형 변수에 1일 더하고 쓰레드B에서 2를 더하면 3이 되어야 하지만, 실제 루프를 돌리면서 테스트를 해보면 3이 대부분 나오지만, 간간히 3이 아닌 값(1아니면 2겠지..)으로 처리되어진다. 빈번한 일이 아니라고 생각할 수 있지만, 실제 테스트를 해보면 엄청 자주 발생을 한다.


이런 경우를 처리하기위해, 한 쓰레드가 변수의 값을 변경하는 동안 다른 쓰레드는 변수의 값을 변경 못하게 해야한다. 그 방법으로 Java에서는 synchronized 블럭을 사용한다. Java의 모든 객체는 Object에서 상속을 받기 때문에 잠금 기능을 포함하고 있다. 



사용법


다음의 코드 블럭이 두개의 쓰레드에서 동시에 실행이 될 경우, synchorized내"{~~~~}"의 코드는 오직 하나의 쓰레드만이 실행되며, 이 실행되고 있는 쓰레드가 이 블럭을 끝내면(마지막"}"를 빠져나가면) 잠금이 풀리게 되고, 이 때 기다리던 다른 쓰레드가 객체를 잠금하고 synchronized 블럭이 실행되게 된다.


synchronized(잠글 객체) {

      // 하나의 쓰레드만 처리되는 영역

}



잠그는 객체별로 따로 처리된다.

같은 class에서 만들어진 instance는 모두 따로 따로 잠금을 할 수가 있다. 같은 class에서 객체가 만들어 져도 각각 다른 객체이기 때문에, 해당하는 class가 잠금이 되는 것은 아니다. 이 내용은 아래의 [static함수의 synchronized] 부분을 유심히 보기 바란다.



객체가 잠겼다고 그 객체의 내용을 다른 쓰레드에서 변경이 불가능한 것은 아니다.

ThreadA에서 객체를 잠금하고 ThreadB에서 값을 변경하면 그냥 변경이 된다.


a = (공유변수);


// ThreadA

synchronized( a ) {

a값변경;

}


// ThreadB

a 값 변경;  // 값이 그냥 변경이 된다.



ThreadA가 a객체의 잠금을 획득해도 ThreadB의 코드로 그냥 변경이 된다. 잠금이라는 표현이 좀 어울리지 않는 상황이고 이를 문제 없이 처리하려면 코드를 다음과 같이 만들어야 한다.


// ThreadB

synchronized( a ) {

      a 값 변경;

}


ThreadB는 ThreadA의 synchronized 블럭이 완전히 실행되어 a의 잠금이 풀리면 그때서야  synchronized블럭에 진입해 a값을 변경한다.


따라서 값을 변경하려는 모든 Thread에서 synchronized 를 사용해야 한다.




static 함수의 synchronized

static이 없는 멤버함수에 synchronized를 쓰면 해당 객체가 잠금이 된다.


// code 1

public synchronoized void func() {

// [code]

}


// code 2

public void func() {


synchronized( this ) {

// [code]

}

}


사실상 위의 code1과 code2는 거의 유사하게 동작한다. 같다고 봐도 무방할 정도다.

해당 instance가 잠금이 되는 것이기 때문에 다음과 같이 다른 메소드에 synchronized가 있는 경우, 두 함수가 동시에 실행 될 수는 없다.



public synchronoized void funcA() {               public void funcA() {
    // code A                                    ->                synchronized( this ) { // code A }
}                                                            }                                  
                                   
                                   
public synchronoized void funcB() {               public void funcB() {
    // code B                                     ->               synchronized( this ) { // code A }
}                                                             }                                 



위에 코드를 여러 쓰레드에서 실행을 하면 해당 instance를 잠그기 때문에, Thread1에서 funcA를 실행하는 동안, 다른 Thread에서는 funcB를 수행하려면 Thread1이 funcA가 끝나야 진입할 수가 있다.

이렇게 되어있는 2개의 메소드는 동시에 실행될 수가 없다. 좀 더 정확한 설명은 바로 다음에 설명을 달아 두겠다.



static으로 지정된 함수에 synchronized를 걸면 해당 class가 잠금이 된다.


// code 1

public static synchronoized void func() {

         // [code]

}


// code 2

public static void func() {


synchronized( this.class ) {

// [code]

}

}


위의 2개의 코드는 거의 유사하게 동작한다. this.class 또한 Object에서 상속받았기 때문에 잠금을 하는 데에 문제가 없다. 이 synchronized되어있는 함수들은 모든 쓰레드에서 동시에 접근을 할 수가 없다.




static이 없는 녀석은 객체별로 잠금이 처리되기 때문에, 다른 객체일 경우 동시에 진입이 가능하지만, static으로 지정되면 class가 잠기기 때문에(단 하나의 객체이므로) 어디에서든 동시에 실행이 될 수 없게 된다.


이전글 : 이 글이 첫 글

다음글 : condition(wait, notify[All] ) : Java 쓰레드 동기화(Synchronization)

condition(wait, notify[All] ) : Java 쓰레드 동기화(Synchronization)

출처: https://jamssoft.tistory.com/200?category=791626 [What should I do?]
condition(wait, notify[All] ) : Java 쓰레드 동기화(Synchronization)

출처: https://jamssoft.tistory.com/200?category=791626 [What should I do?]



Replies
Reply Write