-
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; }