Programming/Java

[자바/Java] Synchronized

코딩하는 랄로 2023. 11. 16. 17:02
반응형

Synchronized

synchronized는 '동기화'를 의미한다. 자바에서 말하는 동기화란 무엇일까?

 

프로그램은 대부분 멀티 쓰레드를 사용하여 성능을 이끌어낸다. 여러 개의 쓰레드가 동시에 여러 작업들을 진행해주므로 단일 쓰레드에 비해 작업 효율면에서 큰 이점을 가지기 때문이다.

 

이러한 멀티 쓰레드 환경에서 주의할 점은 여러 쓰레드가 한 자원을 사용하는 경우에도 해당 자원의 안정성과 신뢰성을 보장해주어야 한다는 것이다. 그렇기 때문에 특정 쓰레드가 자원을 사용하고 있는 경우에, 다른 쓰레드가 해당 자원에 접근하지 못하도록 막는 것이 필요하다.

 

이 때 사용되는 것이 바로 synchronized, 동기화인것이다. 즉, 하나의 자원을 사용하기 위해 여러 쓰레드들을 조화시켜 충돌하지 않도록 하는 것이다. 

 

 

 

Synchronized 사용

자바에서 synchronized를 사용하는 방법에 는 메서드 앞에 선언하는 방식과 동기화 블럭을 만들어 사용하는 방식이 존재한다. 각각에 대해서 알아보기 전에, 먼저 동기화를 사용하지 않는 간단한 코드 예제를 통해 어떠한 문제점이 발생하는지 살펴보자.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 9998
    }
}

class Count {
    private int cnt = 1;
    public int cntInc() { return cnt++; }
}

 

thread 두 개가 돌면서 num을 1씩 증가시키는 코드이다. 원하는 결과는 각각의 쓰레드가 1씩 증가를 십만번 반복하기 때문에 이십만이 나올꺼 같지만, 코드를 돌려보면 그렇지 않다는 것을 볼 수 있다.

 

해당 결과가 나오는 이유가 바로 공통된 자원에 접근하여 작업을 할 때, 동기화를 해주지 않았기 때문이다. 그렇기 때문에 위와 같은 동작 결과가 나오는 것이다. 이러한 결과를 막기 위해서 synchronized를 사용하여 동기화를 해주는 것이 필요하다.

 

 

메소드 synchronized

메소드 선언 부에 synchronized 를 선언해주는 방법이다. 이렇게 되면, 해당 메소드를 thread가 호출할 때 마다 다른 thread가 사용할 수 없게 막는다.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 10000
    }
}

class Count {
    private int cnt = 1;
    public synchronized int cntInc() { return cnt++; }
}

 

 

 

synchronized block선언

또 다른 방식으로는 메소드가 아닌 synchronized block을 선언하여 동기화를 해주는 방법도 있다.

package threadEx;

public class ThreadEx extends Thread{

    public static void main(String[] args) {
        Count cnt = new Count();
        for(int i = 0; i < 100; i++) {
            new Thread(() -> {
                for(int j = 0; j < 100; j++) {
                    System.out.println(cnt.cntInc());
                }
            }).start();
        }
        // 마지막 출력 : 10000
    }
}

class Count {
    private int cnt = 1;
    public int cntInc() {
        synchronized (this) {
            return cnt++;
        }
    }
}

 

synchronized를 사용하면, 공유하는 자원에 대해 안전성과 신뢰성을 보장하여 주지만, 무분별하게 많이 사용하게 된다면 프로그램의 자원을 많이 잡아먹게 되어 역으로 프로그램이 느려지게 되기 때문에 필요한 경우에만 적절히 사용하여야 한다.

반응형