JAVA – Thread Safe Programming

  1. Automic (원자성)

    – 원자성이란 CPU 가 처리하는 하나의 단일 연산
    예를들어 int a = 1 , int a = 1+1 은 원자성 연산이지만, a++ 은 원자성 연산이 아니고,
    [a 의 값을 읽는다] 와 [a의 값을 증가 시킨다] 두 가지 원자 연산의 조합이다.

public class AtomicTest {
 private int a=0;
 public int incrementAndGet(){
  return a++;
 }
 public static void main(String[] args) {
  final AtomicTest test = new AtomicTest();
  for (int i = 0; i < 100; i++) {
   new Thread(){
    public void run(){
     for (int j = 0; j < 1000; j++) {
      System.out.println(test.incrementAndGet());
     }
    }
   }.start();
  }
 }
}

 

위의 코드를 보면 100 개의 Tread 를 생성해서 각 Tread 가 1000개의 값을 증가 시키도록 하는 코드인데, 실제로 실행해보면 100 X 1000 이 아닌 그보다 적은 값이 결과로 출력된다. 그 이유는 아래와 같이 설명될 수 있다.
A Thread : i = 0 Load
B Thread : i = 0 Load (A가 + 하기 전)
A Thread : i = i + 1 저장
B Thread : i = i + 1 저장
결론적으로 두번의 +1 이 이루어 졌으나, 결과 값을 1인 상황이 발생

2. Thread Safety 발생 CASE 예

(1) 명시적으로 Thread 사용시

public class ThreadSafety {

    public static void main(String[] args) throws InterruptedException {
    
        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1);
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        //wait for threads to finish processing
        t1.join();
        t2.join();
    }
}

class ProcessingThread implements Runnable{
    private int count;
    
    @Override
    public void run() {
        for(int i=1; i < 5; i++){
            processSomething(i);
         count++;
        }
    }
}

 

(2) static 변수를 Member 변수로 사용하는 경우

class MyCounter {
  private static int counter = 0;
 
  public static int getCount() {
    return counter++;
  }
}

 

(3) Webservice 를 생성하는 경우
– 아래 예는 WebLogic Webservice

@WebService(name=MyCounter)

class MyCounter {
  private int counter = 0;

 

@WebMethod

 
  public int getCount() {
               return counter++;
  }
}

 

(4) Servlet
– 아래 예는 Spring F/W

@Servicepublicclass

 MyCounter 

{    @Autowired    private int counter = 0;

    public int getCount() {
               return counter++;
    }

}

 

(5) REST 는?
– REST (JAX-RS 기반) 의 경우 Thread Safe 하다고 한다
– 모든 Request 마다 새롭게 자원 할당

3. Thread Safe Programming

(1) Automic Class 사용
– 아래와 같이 Automic Class 를 사용하면 두 Cycle 로 나누어진 작업을 하나로
수행할 수 있게 되며, 이에 따라 위에 설명한 Thread 문제 회피 가능

@WebService(name=MyCounter)

class MyCounter {
  AtomicInteger counter= new AtomicInteger( 0 );

 

@WebMethod

 
  public int getCount() {
               return counter

.incrementAndGet();

  }
}

 


 (2) Vector , HashTable, CuncurrentHashMap, String 사용
– 위의 Class 들은 Thread Safe 하다. 비슷한객체인 HashMap 은 Thread Safe
하지 않다. Thread Safe 가 필요한 경우 위의 변수를 활용한다.

ArrayList<String>crunchifyArrayList=newArrayList<String>(); List<String>synchronizedList=Collections.synchronizedList(crunchifyArrayList); Map<String, String> crunchifyMap = new HashMap<String, String>(); Map<String, String> synchronizedMap = Collections.synchronizedMap(crunchifyMap);

 

 

(3) Local 변수 사용
– Local 변수는 Thread Safe하다.이유 없이 Member변수 사용하지않도록 한다

(4) Synchronized 사용
– Lock & UnLock 을 통해 한시점에 하나의 Thread 만 해당 Method 혹은 Block 을 실행할 수 있도록 Thread 를 제어한다. 사용 방법은 아래와 같이 간단하지만 잘 못 사용시 시스템 성능 저하를 가지고 올 수도 있다.

//Method 동기화
class MyCounter {
  private static int counter = 0;
 
  public static 

synchronized

 int getCount() {
    return counter++;
  }
}

//Block 동기화
private Object obj = new Object();
class MyCounter {
  private static int counter = 0;

  public static 

synchronized

 int getCount() {
   synchronized(obj) {
      return counter++;
   }
  }
}

 

(5) Double Check Locking
– 위의 Synchronized 는 Thread Safety 는 확실하게 하지만 성능 저하라는 큰 문제가 존재함 이를 해결하기 위해 DCL 같은 방법이 사용

public class Singleton {    private volatile static Singleton uniqueInstance;    private Singleton() {   }    public static Singleton getInstance() {        if (uniqueInstance == null) {            // 이렇게 하면 처음에만 동기화 된다            synchronized (Singleton.class) {                if (uniqueInstance == null) {                    uniqueInstance = new Singleton();                }            }        }        return uniqueInstance;    }}

 


(6) ThreadLocal
– ThreadLocal 은 Scope 내에서만 공유되는 변수의 특성상 Scope 을 넘어서 전달하기 위해서는 파라메터 형태로 전달해야하는 불편함을 없에고 동일 Thread 내에서는 변수가 공유되는 특성을 갖는다. 또 이러한 특성을 이용하여 멀티 Thread 환경에서 다른 Thread 에서의 간섭을 배제하는 용도로도 사용가능

// 현재 쓰레드와 관련된 로컬 변수를 하나 생성한다.
ThreadLocal<UserInfo> local = new ThreadLocal<UserInfo>();

// 로컬 변수에 값 할당
local.set(currentUser);

// 이후 실행되는 코드는 쓰레드 로컬 변수 값을 사용
UserInfo userInfo = local.get();

 

(7) Volatile
– 하나의 Thread 에서는 읽기/쓰기 작업을 수행하고 다른 Thread 에서는 읽기 작업만 하는 경우  최신 정보를 읽을 수 있도록 보장해 줌 . 단, 양쪽이 모두 읽기/쓰기 작업이라면
Synchronized 등 다른 방법을 사용해야 함

public class SharedObject {

    public volatile int counter = 0;

}