-
Thread ClassJava 2020. 1. 15. 20:48반응형
Thread Class 생성자
// 새로운 Thread 생성 Thread() // 매개 변수로 받은 target 객체의 run() 메서드 수행하는 Thread 생성 Thread(Runnable target) // 매개 변수로 받은 target 객체의 run() 메서드 수행하고, name이라는 이름을 갖는 Thread 생성 Thread(Runnable target, String name) // name이라는 이름을 갖는 Thread 생성 Thread(String name) // 매개 변수로 받은 group의 ThreadGroup에 속하는 target 객체의 run() 메서드 수행하는 Thread 생성 Thread(ThreadGroup group, Runnable target) /* 매개 변수로 받은 group의 ThreadGroup에 속하는 target 객체의 run() 메서드를 수행하고, * name이라는 이름을 갖는 Thread 생성 */ Thread(ThreadGroup group, Runnable target, String name) /* 매개 변수로 받은 group의 ThreadGroup에 속하는 target 객체의 run() 메서드를 수행하고, * name이라는 이름을 갖는 Thread 생성 * 단, 해당 Thread Stack 크기는 stackSize만큼만 가능 */ Thread(ThreadGroup group, Runnable target, String name, long stackSize) // 매개 변수로 받은 group의 ThreadGroup에 속하는 name이라는 이름을 갖는 Thread 생성 Thread(ThreadGroup group, String name) /* Thread Name * 모든 Thread는 이름이 있음 * 아무런 이름을 지정하지 않을 경우, 기본 이름은 "Thread-n" : n = 쓰레드 생성된 순서에 따라 증가 * 쓰레드 이름 지정시 별도의 이름을 가지게 됨 * 쓰레드 이름 중복시 예외나 에러가 발생하진 않음 * * ThreadGroup * Thread 생성시 Thread를 묶어놓을 수 있음 * Thread의 Group을 묶으면 ThreadGroup 클래스에서 제공하는 여러 메서드를 통해 각종 정보를 얻을 수 있음 * * stackSize * stack의 크기 - Stack이란 클래스와 전혀 상관없이, * 자바 프로세스 시작시 실행 데이터 공간(Runtime data area) 중 하나인 스택이란 공간을 의미 * Thread 생성마다 별도의 스택이 할당됨 * * Thread에서 얼마나 많은 메서드를 호출하는지, 얼마나 많은 Thread가 동시에 처리되는지는 * JVM이 실행되는 OS의 플랫폼에 따라 매우 다르거나, 무시될 수 있음 */
Thread Class 생성자를 상속받아 응용하기
package jamie.thread; public class NameThread extends Thread { // // 아무 매개 변수 없는 Thread() 생성자를 사용하는 것과 동일함 // public NameThread() { // } // 해당 NameThread 객체를 많이 만들어도 이름이 ThreadJamie로 통일됨 public NameThread() { super("ThreadJamie"); } // name 매개 변수를 받아 이름을 지어줌 public NameThread(String name) { super(name); } public void run() { } }
Thread에 매개 변수를 넘기기 - 생성시 받아서 인스턴스 변수로 사용
/* 쓰레드 시작시 진입점 - run() * 쓰레드 시작시킬 때 - start() * * 매개 변수는 어디로 받아야 할까? run() & start() 모두가 매개변수가 없는 메서드 * * Thread Object를 생성할 때 매개 변수를 받고, 인스턴스 변수로 사용한다면 가능 */ package jamie.thread; public class NameThread extends Thread { private int calcNumber; public NameThread(String name, int calcNumber) { super(name); this.calcNumber = calcNumber; } public void run() { calcNumber++; } }
Sleep() 메서드 - 빈번하게 사용됨
Thread Class엔 deprecated된 메서드도 많고, static 메서드도 많음
Thread Class의 static 메서드 대부분은 JVM에 있는 쓰레드를 관리하기 위한 용도로 사용됨
Sleep() 메서드는 예외 중 하나, 해당 쓰레드를 위해 존재하는 static 메서드
유의점, Thread.sleep() 메서드 사용시에는 항상 try-catch로 묶어주어야 함
-
InterruptedException 이상으로 catch해주어야 함
-
sleep()은 InterruptedException을 던질 수도 있다고 선언되어 있기 때문
// 매개 변수로 넘어온 시간(1/1,000초)만큼 대기 static void sleep(long millis) /* 첫 번째 매개 변수로 넘어온 시간(1/1,000초) * + 두 번째 매개 변수로 넘어온 시간(1/1,000,000,000초)만큼 대기 */ static void sleep(long millis, int nanos)
main()의 수행이 끝나더라도, main() 메서드나 다른 메서드에서 시작한 Thread가 종료되지 않으면 해당 자바 프로세스는 끝나지 않음
단, daemon Thread는 예외Thread Class의 주요 메서드
Thread Class에 선언된 static도 아니고 deprecated도 아닌 메서드 중 주요 메서드
Thread 속성을 확인하고 지정하는 메서드, Thread 상태를 통제하기 위한 메서드로 구분 가능
Thread 속성을 확인하고 지정하는 메서드
// Thread 시작시 진입점, 구현해야 하는 메서드 void run() // Thread의 고유 ID를 리턴, JVM에서 자동 생성 long getId() // Thread의 이름을 리턴 String getName() // Thread의 이름을 지정 void setName(String name) /* Thread의 Priority(우선순위) * 대기하고 있는 상황에서 더 먼저 수행할 수 있는 순위 * 대부분 기본 값으로 사용하는 것을 권장(우선순위 잘못 변경시 장애로 연결됨) * * 우선순위와 관계있는 상수 3가지 = 우선순위는 숫자보다는 상수 이용을 권장 * MAX_PRIORITY = 가장 높은 우선순위, 값은 10 * NORM_PRIORITY = 일반 우선 순위, 값은 5 * MIN_PRIORITY = 낮은 우선 순위, 값은 1 */ // Thread의 우선 순위 확인 void getPriority() // Thread의 우선 순위 지정 void setPriority(int newPriority) /* Daemon Thread가 아닌 사용자 Thread는 JVM이 해당 Thread가 끝날 때 까지 기다린 후 종료 * Daemon Thread의 경우, 해당 Thread가 수행되던 말던 상관 없이 JVM 종료 가능 * 단, 해당 Thread가 시작하기(start() 메서드 호출) 전에 Daemon Thread로 지정되어야 함 * = 시작시 Daemon Thread로 지정할 수 없음 */ // Thread가 Daemon인지 확인 boolean isDaemon() // Thread의 Daemon 여부 지정 void setDaemon(boolean on) // Thread의 Stack 정보를 확인 StackTraceElement[] getStackTrace() // Thread의 상태 확인 Thread.State getState() // Thread의 Group 확인 ThreadGroup getThreadGroup()
Thread와 관련이 많은 synchronized
synchronized = 자바의 예약어 하나
변수명 또는 Class 명으로 사용 불가
어떤 Class나 Method가 Thread safe하려면, synchronized를 사용해야 함
사용 방법은 두 가지
-
synchronized methods : 메서드 자체를 synchronized로 선언하는 방법
-
synchronized statements : 메서드 내 특정 문장만 synchronized로 감싸기
synchronized methods
메서드 선언 문에 synchronized를 넣어주기
일반 메서드와 차이점
-
synchronized methods의 경우 해당 객체의 synchronized methods에 2개의 threads가 접근하던, 100개의 threads가 접근하던 한 순간에는 하나의 thread만 해당 메서드를 수행하게 됨
-
메서드에서 공유하는 인스턴스 변수를 수정하려고 할 때, 접근한 threads가 동시에 연산을 수행하여 값이 꼬이는 경우가 발생할 수 있음
-
매개 변수나 메서드에서만 사용하는 지역변수만 다루는 메서드의 경우 전혀 synchronized로 선언할 필요가 없음
단점
-
성능상 문제점이 발생할 수 있음
-
필요한 부분을 제외한 코드 처리시에도 대기를 하게 되고, 대기 시간이 쌓이면 성능 문제
-
그럴 경우엔 synchroized statements(블럭)을 사용하면 해당 단점을 방지할 수 있음
// 일반 메서드 public void plus(int value) { amount += value; } // synchronized Method public synchronized void plus(int value) { amount += value; }
synchronized statements (synchronized 블럭)
synchronized methods의 문제를 해결하기 위한 방법
synchronized(this) { } 내의 연산만 동시에 여러 threads에서 처리하지 않겠다는 의미
public void plus(int value) { synchronized(this) { amount += value; } }
보통 일반적으로 synchronized(this)의 this 부분에는 잠금 처리를 하기 위한 별도의 객체를 선언함
lock이나 this는 모두 문지기로 볼 수 있음 → 하나의 thread만 일을 할 수 있도록 허용
Object lock = new Object(); public void plus(int value) { synchronized(lock) { amount += value; } }
여러 synchronized 블럭을 사용한 경우에는, 문지기 객체를 여러 개 만들어서 사용 가능
한 개의 문지기 객체를 같이 사용하는 경우에는, 다른 작업일때도 홀딩되는 효과
private int amount; private int interest; private Object interestLock = new Object(); private Object amountLock = new Object(); public void addInterest(int value) { // interestLock은 하나만 쓰이므로 해당 작업에서만 잠김 synchroinzed(interestLock) { interest += value; } // amountLock은 같이 쓰이므로 하나 실행되면 끝날 때까지 같이 잠김 synchronized(amountLock) { System.out.println("별건 안함"); } } public void plus(int value) { // amountLock은 같이 쓰이므로 하나 실행되면 끝날 때까지 같이 잠김 synchronized(amountLock) { amount+=value; } }
Synchronized 사용시 유의할 점
같은 객체를 참조할 때만 유효
// 같은 객체 참조 = 유효 CommonCalculate calc = new CommonCalculate(); ModifyAmountThread thread1 = new ModifyAmountThread(calc,true); ModifyAmountThread thread2 = new ModifyAmountThread(calc,true); // 다른 객체 참조 = 무효 ModifyAmountThread thread1 = new ModifyAmountThread(new CommonCalculate(),true); ModifyAmountThread thread2 = new ModifyAmountThread(new CommonCalculate(),true);
여러 threads에서 하나의 객체에 있는 인스턴스 변수를 동시에 처리할 때 발생할 수 있는 문제를 해결하기 위해 필요한 것임을 명시 = 모든 method에 절대 해당 예약어를 추가하지 말 것
-
인스턴스 변수가 선언되어 있지 않으면 사용할 일이 없음
-
인스턴스 변수가 선언되어 있더라도, 변수가 선언되어 있는 객체를 다른 thread에서 공유할 일이 전혀 없다면 사용할 일이 없음
StringBuffer와 StringBuilder
StringBuffer = Thread Safe → Synchronized 블럭으로 주요 데이터 처리 부분을 감싸둠 → 여러 threads에서 공유해야하는 경우
StringBuilder = Thread Safe하지 않음 → Synchronized 블럭 사용하지 않음 → 여러 threads에서 공유할 일이 없는 경우Thread 상태를 확인/통제하기 위한 메서드
// Thread 상태 확인 Thread.State getState() // 수행중인 Thread가 중지할 때까지 대기 void join() /* 매개 변수에 지정된 시간만큼(1/1,000초) 대기 * 0일 경우, join()과 동일 */ void join(long millis) /* 첫 번째 매개 변수에 지정된 시간(1/1,000초) * + 두 번째 매개 변수에 지정된 시간(1/1,000,000,000초)만큼 대기 * 첫 번째 매개 변수가 음수이거나, 두 번째 매개 변수가 0 ~ 999,999 사이의 값이 아니면 * IllegalArgumentException 예외 발생 */ void join(long millis, int nanos) /* 수행중인 Thread에 중지 요청 * InterruptedException을 발생시키면서 중단시킴 * sleep(), join(), wait() 호출 상태에서만 Thread 중단 * * sleep()과 join() 에서발생하는 예외 - (try - catch로 묶어주라고 했던 예외) * sleep()과 join() 메서드와 같이 대기 상태를 만드는 메서드가 호출되었을 때엔 * interrupt() 메서드를 호출할 수 있다는 의미 * * Object Class의 wait() 메서드 호출시에도 사용 가능 * * Thread 시작 전, 종료 후에는 interrupt() 호출시 예외나 에러 없이 다음 문장으로 넘어감 */ void interrupt() /* 주의 * stop() 메서드는 deprecated 되었으며 * Thread를 멈출 때는 위의 interrupt() 메서드를 사용 */ /* 현재 수행중인 Thread가 해당 Thread를 수정할 수 있는 권한이 있는지 확인 * 권한이 없다면, SecurityException 예외 발생 */ void checkAccess() /* Thread가 살아있는지 확인 * 해당 Thread의 run() 메서드 종료 여부를 확인하는 것 */ boolean isAlive() /* isInterrupted()와 interrupted()가 필요한 이유 * in /* run() 메서드가 정상적으로 종료되지 않고 * interrupt() 메서드 호출을 통해 종료되었는지 확인 */ boolean isInterrupted() /* 현재 Thread가 중지되었는지 확인 * isInterrupted() 메서드 - 다른 Thread에서 확인 * interrupted() - 본인 Thread 확인 static boolean interrupted()
Thread Class의 State Enum Class
Java의 Thread Class에는 State Enum Class가 있음
public static으로 선언되어 있음
어떤 Thread건 NEW → 상태(NEW/TERMINATED 제외한) → TERMINATED
// Thread Object는 생성되었지만, 아직 시작 전인 상태 NEW // Thread 실행중인 상태 RUNNABLE // Thread 실행 중지 상태, 모니터 락(monitor lock)이 풀리기를 기다리는 상태 BLOCK // Thread 대기중인 상태 WAITING // 특정 시간만큼 Thread 대기중인 상태 TIME_WATIING // Thread 종료 상태 TERMINATED
Thread의 순환
Thread의 주요 static 메서드
// 현재 Thread가 속한 ThreadGroup의 Thread 중 살아있는 Thread 개수를 리턴 static int activeCount() // 현재 수행중인 Thread의 객체를 리턴 static Thread currentThread() // Console창에 현재 Thread의 Stack 정보를 출력 static void dumpStack()
유의
interrupt() 메서드는 join(), sleep(), wait() 메서드가 호출된 상태에서만 Thread를 중단
만약 어플리케이션의 다른 메서드가 계속 실행중이고, 위 세 개의 메서드 중 하나가 호출되지 않는다면, 아무리 interrupt()를 호출하더라도 효과가 없음 = 멈추지 않음
// Case 1 package jamie.thread; import java.util.HashMap; import java.util.Hashtable; public class InfiniteThread extends Thread { public void run() { while(true) { String str = "String."; new HashMap(10000); new Hashtable(10000); } } } // Case 2 package jamie.thread; import java.util.HashMap; import java.util.Hashtable; public class InfiniteThread extends Thread { public void run() { while(true) { String str = "String."; new HashMap(10000); new Hashtable(10000); if(Thread.interrupted()) return; } } } /* Case 1 * interrupt() called * isAlive = true // 해당 Thread가 살아있음 * isInterrupted = true // interrupt는 호출되었지만, 특정 메서드들이 실행중이 아니라 효과가 없음 * * Case 2 * interrupt() called * isAlive = false // 해당 Thread가 멈춤 * isInterrupted = false // interrupt 되었지만, Thread가 중지된 이후이기 때문 */ public void infinite() { InfiniteThread thread = new InfiniteThread(); thread.start(); try { Thread.sleep(400); thread.interrupt(); System.out.println("interrupt() called"); thread.join(500); } catch (InterruptedException ie) { ie.toString(); } System.out.println("is Alive = " + thread.isAlive()); System.out.println("isInterrupted = " + thread.isInterrupted()); }
반응형'Java' 카테고리의 다른 글
JAVA I/O (0) 2020.02.06 Thread 관련 - Object, ThreadGroup, ThreadLocal, volatile (0) 2020.01.16 Thread 개념 및 Runnable Interface VS Thread Class (0) 2020.01.13 java.math.BigDecimal (0) 2020.01.13 java.util Package (0) 2020.01.13 -