본문 바로가기
백엔드/Java

멀티스레드 환경에서의 Thread-safe 테스트

by david100gom 2020. 2. 16.

멀티스레드 환경에서의 Thread-safe  테스트

스프링 프레임워크의 기본 bean scope 는 singleton 이며, 멀티스레드 환경에서 클래스내의 멤버변수로 인한 데이터가 꼬이는 문제가 발생한다. 가능하면 멤버변수는 DI 를 하는 bean 외에는 사용하지 않는것이 정신 건강에 좋다. 또한, bean scope 를 prototype 으로 구성하면 생명주기를 스프링 프레임워크가 관리하지 않기 때문에 객체 사용이 끝나면 직접 소멸시켜줘야 한다. 그러지 않으면 스레드 락이 생겨 애플리케이션 성능에 심각한 문제가 발생한다.

아래 4개 클래스를 생성후, StaticMethodMultiThreadTest 를 실행하여 멤버변수의 위치에 따른 결과값을 테스트한다.

public class Member {

    String id;
    String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
class TestThreadClass extends Thread {
    static int i = 1;
    public void run() {
        if(i%2 == 0) {
            TestThreadClass2 t2 = TestThreadClass2.getInstance();
            t2.testStaticMethod(this.getName());
        }else{
            TestThreadClass2 t2 = TestThreadClass2.getInstance();
            t2.testStaticMethod2(this.getName());
        }
        i++;
    }
}
public class TestThreadClass2 {

    long threadSafeInt2 = 0;
    Member member = new Member();

    private static TestThreadClass2 uniqueInstance;
    private TestThreadClass2() {
    }
    
    // singleton
    public static TestThreadClass2 getInstance() {

        if (uniqueInstance == null ) {
            synchronized( TestThreadClass2.class ) {
                if(uniqueInstance == null ) {
                    uniqueInstance = new TestThreadClass2();
                }
            }
        }

        return uniqueInstance;
    }

    public void testStaticMethod(String threadName) {
        System.out.println("threadSafeInt Start : " + threadName);
        long threadSafeInt = 0;
        threadSafeInt++; // 항상 1 : 로컬 변수의 값은 multiple thread에 safe
        System.out.println("threadSafeInt : " + threadSafeInt);
        if(threadSafeInt != 1) {
            System.out.println("threadSafeInt It is not thread safe : " + threadSafeInt);
        }
            System.out.println("threadSafeInt End : " + threadName);
    }
 
    public void testStaticMethod2(String threadName) {
 
        member.setId("ID : "+String.valueOf(threadSafeInt2));
        member.setName("이름 : "+String.valueOf(threadSafeInt2));
 
        System.out.println("testStaticMethod2 Start : " + threadName);
        threadSafeInt2++; // 계속 증가 : not thread safe
        System.out.println("testStaticMethod2 threadSafeInt : " + threadSafeInt2);
        if (threadSafeInt2 != 1) {
            System.out.println("testStaticMethod2 It is not thread safe : " + threadSafeInt2);
        }
        System.out.println("testStaticMethod2 End : " + threadName);
 
        // 결과 : ID 와 Name 이 같은 값으로 나올것 같지만, 실제로는 데이터가 섞임. not thread safe
        System.out.println("testStaticMethod2 Member id : " + member.getId()+ " name : "+member.getName());
    }

}
public class StaticMethodMultiThreadTest {
    public static void main(String arg[]) {
        int exeCnt = 100;
        for (int i = 0; i < exeCnt; i++) {
            new Thread(new TestThreadClass()).start();
        }
    }
}

댓글