이전 포스팅 에선 n개의 스레드를 생성하여 실행하여 보았습니다.


작업의 동시성을 위해 thread는 매우 유용하게 사용되는데요,
하지만 공유되는 자원에 대해서 동기화 처리가 되지 않는다면 문제를 일으킬 수 있습니다.
이번 포스팅에선 thread를 동기화하여 안전하게 공유 자원을 사용하는 방법에 대해서 알아 보도록 하겠습니다.

 


비동기화

먼저, 동기화 처리가 되지 않는 thread의 결과값을 확인해보겠습니다
'이것이 자바다'의 나오는 예제입니다.

Calculator

public class Calculator {

    private int memory;

    public void setMemory(int memory) {
        this.memory = memory;

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": " + this.memory);
    }
}

User1

public class User1 extends Thread {

    private Calculator calculator;

    public void setCalculator(Calculator calculator) {
        this.setName("User1"); // set thread name
        this.calculator = calculator;
    }

    public void run() {
        calculator.setMemory(100);
    }
}

User2

public class User2 extends Thread {

    private Calculator calculator;

    public void setCalculator(Calculator calculator) {
        this.setName("User2"); // set thread name
        this.calculator = calculator;
    }

    public void run() {
        calculator.setMemory(50);
    }
}

Main

public class MainThreadExample {

    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        User1 user1 = new User1();
        user1.setCalculator(calculator);
        user1.start();

        User2 user2 = new User2();
        user2.setCalculator(calculator);
        user2.start();
    }
}

결과

User1: 50
User2: 50

User1은 setMemory에서는 100을, User2 setMemory에서는 50을 설정하였습니다.
그러나 User1의 결과는 100이 아닌 50이 설정되었습니다.

그이유는 즉슨, User1과 User2 Calculator를 공유하여 사용하고 있습니다
다만, memory set후 바로 출력하는것이 아닌 2초간의 sleep time이 있기 때문입니다
따라서 원하는 값을 얻기 위해서는 공유 자원의 대한 제어가 필요합니다


동기화 블록 설정

스레드가 사용중인 객체를 다른 스레드가 변경할 수 없도록 하려면 스레드 작업이 끝날떄 까지
객체에 잠금을 걸어 다른 스레드가 사용할 수 없도록 해야합니다.


이처럼 멀티스레드 프로그램에서 단 하나의 스레드만 실행할 수 있는 코드 영역을 임계 영역(critical section)이라고 합니다.
자바는 임계 영역을 지정하기 위해 동기화 메서드블록을 제공합니다

동기화 메서드

public synchronized void setMemory(int memory) {
    this.memory = memory;

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + ": " + this.memory);
}

동기화 블록

public void setMemory(int memory) {
    synchronized (this) {
        this.memory = memory;

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ": " + this.memory);
    }
}

이외에도 'volatile' 이라는 키워드가 있습니다.
'volatile'은 변수에 적용하여 Cache memory가 아닌 main memory에서 동작하게 설정됩니다.
즉, 멀티스레드 환경에서 임계영역이 아닌 main memory에서 동작하기 때문에
여러 스레드가 공유 자원에 대해 동시에 '읽기-쓰기' 작업을 진행한다면 원자성을 보장해주지 않습니다.

따라서, 위와 같은 공유자원에 여러 스레드가 '읽기-쓰기' 작업을 수행한다면,
임계영역을 보장해주는 synchronized 키워드를 사용해야 합니다

참조

이것이 자바다 - 신용권님 저

블로그 이미지

사용자 yhmane

댓글을 달아 주세요