반응형

 

 

package singletoneTest;

public class Object1 {
    private static Object1 o; // 싱글톤
    private static int oNumber; // 상태 저장

    public static Object1 getInstance(){
        if(o == null)
            o = new Object1();
        return o;
    }

    public int func(int n){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return n;
    }

    public int sfunc(int n){
        oNumber = n;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return oNumber;
    }
}

 

Object1 클래스는 싱글톤으로 사용

 

func()함수 - 입력받은 n을 return

sfunc()함수 - 입력받은 n을 oNumber 필드에 대입하고 oNumber를 return

 

 

 

 

public class MainClass1{
    public static void main(String[] args) {
        Thread t1 = new Thread1();
        Thread t2 = new Thread2();
        Thread t3 = new Thread3();

        t1.start();
        t2.start();
        t3.start();
    }
}

class Thread1 extends Thread{
    private int number = 1;
    @Override
    public void run() {
        while(true){
            Object1 o = Object1.getInstance();

            int n = o.func(number);
            System.out.println(n + " Thread1 의 넘버");
            int n1 = o.sfunc(number);
            System.out.println(n1 + " Thread1 의 넘버, 상태 공유 사용할 경우");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Thread2 extends Thread{
    private int number = 2;
    @Override
    public void run(){
        while(true){
            Object1 o = Object1.getInstance();

            int n = o.func(number);
            System.out.println(n + " Thread2 의 넘버");
            int n1 = o.sfunc(number);
            System.out.println(n1 + " Thread2 의 넘버, 상태 공유 사용할 경우");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Thread3 extends Thread{
    private int number = 3;
    @Override
    public void run(){
        while(true){
            Object1 o = Object1.getInstance();

            int n = o.func(number);
            System.out.println(n + " Thread3 의 넘버");
            int n1 = o.sfunc(number);
            System.out.println(n1 + " Thread3 의 넘버, 상태 공유 사용할 경우");

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

 

3개의 쓰레드를 생성한 후 

각 쓰레드는 자신의 number 변수를 func(), sfunc() 메서드에 각각 넣은 후 출력

 

 

 

 

 

 

sfunc() - 자신의 번호를 출력하지 못하는 문제가 발생

이유는 공유 변수 oNumber가 스레드에 안전하지 않기 때문이다.

 

func() - 자신의 번호에 맞게 정확하게 출력한다.

 


 

스레드가 생성되면 스택이 새로 생성되고, 생성된 스택에서 메서드를 호출하여 사용한다.

각 스레드 스택의 스택 프레임은 지역 변수들이 사용된 후 소멸한다.

지역 변수가 Heap영역을 참조하여 사용한다면 공유 데이터 사용에 주의해야한다. (싱글톤은 공유 데이터를 갖지 않는 것이 원칙)

 

sfunc() - Object1의 상태 데이터(공유 데이터)를 사용하므로 스레드에 안전하지 못하다.

func() -  Object1의 메서드만 사용하고 지역 변수는 자신이 넘겨주므로, Heap의 상태 데이터(공유 데이터)는 사용하지 않아 스레드에 안전하다.

 

 

 

Thread Safe

 

(1) 인스턴스 데이터를 사용하지 않는다. (싱글톤)

싱글톤 객체를 여러 쓰레드에서 사용할 때는 Heap의 인스턴스 데이터(상태 데이터)를 사용하지 않게 설계해야한다.

데이터는 스레드가 넘겨주고, Heap의 인스턴스는 메서드만 사용한다.

 

(2) 스레드마다 인스턴스를 생성하여 사용한다. (프로토타입)

Heap의 인스턴스 데이터를 사용해야 하는 경우엔 스레드마다 사용하는 인스턴스를 새로 생성하여 사용한다.

사용자에 따라 상태 정보가 달라지는 DTO, Entity가 싱글톤이 아닌 이유이기도 하다.

 

(3) Lock을 걸어서 다른 스레드가 접근하지 못하게 한다.

 

 

 

 


 

반응형

+ Recent posts