-
Thread 관련 - Object, ThreadGroup, ThreadLocal, volatileJava 2020. 1. 16. 20:18반응형
Object 클래스에 선언된 Thread와 관련된 메서드들
/* 다른 Thread가 Object 객체에 대한 notify() 메서드나 notifyAll() 메서드를 * 호출할 때까지 현재 Thread가 대기하고 있도록 함 * WAITING 상태로 변경됨 */ void wait() /* wait() 메서드와 동일한 기능을 제공 * 매개 변수에 지정한 시간만큼만 대기 * 매개 변수 시간을 넘어섰을 때, 현재 Thread는 다시 깨어남 * 시간은 밀리초로 1/1,000초 단위 - 1초일 경우 1000 */ void wait(long timeout) /* wait() 메서드와 동일한 기능을 제공 * 보다 자세한 밀리초(1/1,000) + 나노초(1/1,000,000,000)만큼 대기 * 나노초는 0 ~ 999,999까지 지정 가능 */ void wait(long timeout, int nanos) /* 모니터 * = lock 객체처럼 Thread Safe하게 수행하도록 도와주는 객체 * = synchronized(monitor) { } 이런식으로 사용 */ // Object 객체의 모니터에 대기하고 있는 단일 Thread를 깨움 - 먼저 대기하는 Thread부터 void notify() // Object 객체의 모니터에 대기하고 있는 모든 Thread를 깨움 void notifyAll()
사용 예
package jamie.thread; public class StateThread extends Thread { private Object monitor; // monitor라는 이름의 객체를 매개변수로 받아 인스턴스 변수로 선언 public StateThread(Object monitor) { this.monitor = monitor; } public void run() { try { // Thread를 실행중인 상태로 만들기 위해 간단하게 loop를 돌며 String 객체 생성 for(int loop = 0; loop < 10000; loop++) { String a = "A"; } // synchronized 블럭 안에서 monitor 객체의 wait() 메서드를 호출 synchronized(monitor) { monitor.wait(); } System.out.println(getName) + " is notified"); // wait() 상황이 끝나면 1초간 대기했다가 해당 쓰레드 종료 Thread.sleep(1000); } catch (InterruptedException ie) { ie.printStackTrace(); } } }
package jamie.thread; public class RunThreads { public static void main(String[] args) { RunTreads threads = new RunThreads(); threads.checkThreadState(); } public void checkThreadState() { // StateThread의 매개 변수로 넘겨줄 monitor라는 Object 클래스 작성 Object monitor = new Object(); StateThread thread = new StateThread(monitor); try { System.out.println("thread state = " + thread.getState()); // Thread 객체를 생성하고 시작 thread.start(); System.out.println("thread state(after start) = " + thread.getState()); Thread.sleep(100); System.out.println("thread state(after 0.1 sec) = " + thread.getState()); // monitor 객체를 통해 notify() 메서드 호출 synchronized(monitor) { monitor.notify(); } Thread.sleep(100); System.out.println("thread state(after notify) = " + thread.getState()); // Thread가 종료될 때까지 기다린 후 상태 출력 thread.join(); System.out.println("thread state(after join) = " + thread.getState()); } catch (InterruptedException ie) { ie.printStackTrace(); } } } /* 출력 결과 * thread state = NEW * thread state(after start) = RUNNABLE * thread state(after 0.1 sec) = WAITING * Thread-0 is notified. * thread state(after notify) = TIMED_WAITING * thread state(after join) = TERMINATED */
ThreadGroup에서 제공하는 주요 메서드들
ThreadGroup - Thread의 관리를 용이하게 하기 위한 Class
기본적으로 tree 구조를 가짐 - 즉 다른 그룹에 속할 수도 있고, 다른 그룹을 포함할 수도 있음
// 실행중인 Thread의 개수를 리턴 int activeCount() // 실행중인 ThreadGroup의 개수를 리턴 int activeGroupCount() /* enumerate의 리턴값 = 배열에 저장된 값의 개수 * 위의 개수를 파악하는 메서드로 갯수 파악 후 배열을 맞춰 생성하면 됨 */ // 현재 ThreadGroup에 있는 모든 Thread를 매개 변수로 넘어온 Thread 배열에 담음 int enumerate(Thread[] list) /* 현재 ThreadGroup에 있는 모든 Thread를 매개 변수로 넘어온 Thread 배열에 담음 * 두 번째 매개 변수가 true이면 하위에 있는 ThreadGroup의 Thread 목록도 포함됨 */ int enumerate(Thread[] list, boolean recurse) // 현재 ThreadGroup에 있는 모든 ThreadGroup을 매개 변수로 넘어온 ThreadGroup 배열에 담음 int enumerate(ThreadGroup[] list) /* 현재 ThreadGroup에 있는 모든 ThreadGroup을 매개 변수로 넘어온 ThreadGroup 배열에 담음 * 두 번째 매개 변수가 true라면 하위의 ThreadGroup 목록도 포함됨 */ int enumerate(ThreadGroup[] list, boolean recurse) // ThreadGroup의 이름을 리턴 String getName() // 부모 ThreadGroup을 리턴 ThreadGroup getParent() // ThreadGroup 상세정보 출력 void list() // 지금 ThradGroup에 속한 Thread들을 Daemon으로 지정 void setDaemon(boolean daemon)
ThreadLocal - 각 Thread에서 혼자 쓸 수 있는 값
synchronized - 여러 Thread에서 데이터를 공유할 때 발생하는 문제를 해결
ThreadLocal - Thread 별로 서로 다른 값을 처리해야 할 필요가 있을 때 사용
ThreadLocal
-
ThreadLocal에 저장된 값은 해당 Thread에서 고유하게 사용할 수 있음
-
ThreadLocal 클래스의 변수는 private static final로 선언
-
ThreadLocal 클래스에 선언되어 있는 메서드 : set(), get(), remove(), initialValue()
-
사용이 끝난 후에는 remove() 메서드를 호출해주는 습관을 들일 것
remove() 메서드를 호출하는 이유
web 기반의 application에서는 Thread를 재사용하기 위해 ThreadPool이라는 것을 사용
ThreadPool을 사용하면, Thread가 시작된 후 그냥 끝나는 것이 아니기 때문에 remove()를 사용해서 값을 지워줘야만 해당 Thread를 다음에 사용할 때 쓰레기 값이 들어있지 않게 됨예제
ThreadLocal을 사용하지 않고 OtherLogic Class에서 임의로 만든 값을 사용하려면?
- printMyNumber() 메서드 호출시 매개 변수로 넘기기, 단) OtherLogic에서 호출하는 다른 메서드에서 해당 값을 사용하려면 또 매개 변수로 넘겨줘야 함
- LocalUserThread에서 인스턴스 변수를 선언하여 사용, 단) 구현이 매우 복잡해질 수 있음
package jamie.thread; import java.util.Random; public class ThreadLocalSample { /* local이라는 ThreadLocal 객체 생성 * ThreadLocal은 제네릭한 클래스 * - 객체를 생성하기 위해서는 각 Thread에서 고유하게 사용할 데이터 타입을 지정해줘야 함 * 예제는 Integer */ private final static ThreadLocal<Integer> local = new ThreadLocal<Integer>(); private static Random random; static { random = new Random(); } public static Integer generateNumber() { int value = random.nextInt(45); // ThreadLocal 클래스의 set() 메서드를 이용해 저장하고자 하는 값 할당 local.set(value); return value; } public static Integer get() { // ThreadLocal 클래스의 get() 메서드를 이용하여 값을 꺼냄 return local.get(); } public static void remove() { // local 객체에 저장되어 있는 값을 제거하기 위해 remove() 메서드 사용 local.remove(); } } // ThreadLocal 클래스 확장 사용시, initalValue() 메서드를 Override하여 초기 값 지정도 가능함
// 현재 Thread의 이름과 ThreadLocalSample의 get() 메서드를 사용하여 값을 읽음 package jamie.thread; public class OtherLogic { public void printMyNumber() { System.out.println(Thread.currentThread().getName() + " OtherLogic value = " + ThreadLocalSample.get()); } }
package jamie.thread; public class LocalUserThread extends Thread { public void run() { /* 간단하게 ThreadLocalSample의 generateNumber() 메서드 호출 * 임의의 변수를 ThreadLocal에 저장하고 리턴 */ int value = ThreadLocalSample.generateNumber(); System.out.println(this.getName() + " LocalUserThread value = " + value); OtherLogic otherLogic = new OtherLogic(); // OtherLogic이라는 Class의 printMyNumber란 메서드 호출 otherLogic.printMyNumber(); // ThreadLocal에서 값을 지움 ThreadLocalSample.remove(); } }
package jamie.thread; public class RunThreads { public static void main(String[] args) { RunTreads threads = new RunThreads(); threads.runLocalUserThread(); } public void runLocalUserThread() { LocalUserThread threads[] = new LocalUserThread[3]; for (LocalUserThread thread : threads) { thread = new LocalUserThread(); thread.start(); } } } /* 실행 결과 * Thread-1 LocalUserThread value=20 * Thread-1 OhterLogic value=20 * Thread-2 LocalUserThread value=01 * Thread-2 OtherLogic value=01 * Thread-3 LocalUserThread value=15 * Thread-3 OtherLogic value=15 * * LocalUserThread에서 출력한 값과 OtherLogic에서 출력한 값이 동일함 * Thread별로 값을 절대 공유하지 않음 */
Java의 volatile
변수 선언시에만 사용되는 예약어
예제
package jamie.thread; public class VolatileSample extends Thread { private double instanceVariable = 0; // 1. volatile 사용 // private volatile double instanceVariable = 0; void setDouble(double value) { this.instanceVariable = value; } public void run() { // instanceVariable이 0이면 계속 while문을 돌림 while (instanceVariable == 0); System.out.println(instanceVariable); } /* 2. 대기시간을 1밀리초 줌 public void run() { try { while (instanceVariable == 0) { Thread.sleep(1) } } catch (Exception e) {} System.out.println(instanceVariable); } */ }
package jamie.thread; public class RunThreads { public static void main(String[] args) { RunTreads threads = new RunThreads(); threads.runLocalUserThread(); } public void runValatileSample() { VolatileSample sample = new VolatileSample(); sample.start(); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println("Sleep ended !!!"); sample.setDouble(-1); System.out.println("Set value is completed !!!"); } } /* 결과 * Sleep ended !!! * Set value is completed !!! * * 그런데 sample Thread가 종료되지 않음 * 원인 : CPU Cache 때문 * Thread에서 수행되는 변수의 값을 반복적으로 참조할 때는 "메인 메모리"에 저장되지 않음 * "CPU 캐시"에 저장되고 참조됨. 값을 바꾸게 되면, CPU 캐시의 다른 곳에 또 저장되는데, * 진행중인 쓰레드는 기존 값(0)을 참조함. 즉 효과 없음. * * 1. volatile으로 선언된 Thread의 인스턴스 변수의 경우, 해당 변수 값이 바뀌면 추적을 해줌 * 같은 객체에 있는 변수는 모든 쓰레드가 같은 값을 바라보게 됨 * 단) 남발시 성능 저하 발생 * * 모든 Thread의 인스턴스 변수에 volatile이라고 적어주지 않았다고 전부 데이터가 꼬이는건 아닌데, * 왜 예제는 volatile을 적지 않았을 때 문제가 생겼을까? * - JIT 컴파일러가 Optimization(최적화 작업)을 캐시간에 두고 최적화되었기 때문 * 2. 즉 최적화가 되지 않아 캐시간 데이터가 다른 값을 보지 않으면 volatile을 사용할 필요가 없음 * * volatile은 데이터에 문제가 있을 경우에만 사용 */
그 외, ThreadPool을 비롯한 Thread를 처리하는 Class는 java.util.concurrent 패키지의 Class를 참고
반응형'Java' 카테고리의 다른 글
Java NIO (0) 2020.02.06 JAVA I/O (0) 2020.02.06 Thread Class (0) 2020.01.15 Thread 개념 및 Runnable Interface VS Thread Class (0) 2020.01.13 java.math.BigDecimal (0) 2020.01.13 -