Java 정리 32
27 Dec 2018
Reading time ~4 minutes
Java 정리 32 - Thread
Thread
-
동시에 일을 처리해야할 때 사용
- 동시에 여러 method를 호출가능
-
Thread는 작은 process
- CPU가 한번에 처리하는 일의 단위
- 사용자가 실행하는 프로그램
- OS가 운영하기 위해 실행하는 프로그램
- 메모리에 큰 프로세스를 올리는게 아니라 여러프로세스들의 조각(Thread)를 올려 CPU에게 계속 제공, 처리
- 원래 하나에 하나의 프로세스만 처리되는데 여러개의 프로세스가 처리되는 것처럼 보이는 것.
- CPU가 한번에 처리하는 일의 단위
-
자바는 Multi Thread 지원
- cmd는 싱글 Thread,cmd에서 eclipse.bat을 실행하면 다른 작업을 못 한다.
- Thread 사용된 코드는 종료시간을 측정할 수 없다.
-
Runnable interface, Thread class로 Thread 사용가능
- Runnable interface는 생명주기를 관리할 수 없음
- Thread class는 생명주기 관리 method 존재
-
자바는 선점형 Thread
- 작업관리자가 필요없음
- 간단하게 구현되고 사용가능
- 호출되는 시점에 줄을 서서 선점 순서대로 처리가 됨.
- 또다른 방식은 시분할 OS
- 작업관리자의 역할이 매우 중요
- 작업관리자가 시간을 프로세스들에게 분배, CPU를 점유가능케 함
- 작업관리자가 필요없음
- Thread가 CPU를 점유하고선 끝나지 않는 상태 Deadlock
- CPU를 점유하는 Thread를 강제로 뜯어내는 것 = Interrupt
- 전원을 뽑는 것도 interrupt의 일부
- 작업관리자에서 프로세스강제 종료
- cmd에서 ^c로 종료 등
Thread 생명주기
- 선점형 Thread는 누가 먼저 실행될지 모름, start()로 running
- Field로 우선순위를 바꿀 수 있으나 잘 바꾸진 않는다.(통제불가)
- yield() 를 사용하여 순서를 양보할 수 있다.
-
sleep이란 메서드로 Thread를 block상태로 만들 수 있다.
- Thread가 block상태가 안되어 InterruptedException 발생가능
- millis만큼 쉬다가 자동으로 Running 상태로 돌아옴
-
wait은 Thread를 쓰든 쓰지 않든 사용가능, sleep과 동일하게 Thread block가능
- Object클래스에 존재, wait으로 멈춘 Thread는 notify 메서드를 이용하여 직접 깨워야 한다.
- 하나만 깨울 땐 notify, 모든 Thread를 깨울 땐 notifyAll 메서드를 이용한다.
- stop()메서드로 Thread를 중지시킬 수 있으나 중지시키면 사용중이던 자원이 불완전한 상태로 남겨지기 때문에 사용안하는 deprecated method
Thread class를 상속받아 Thread 사용
-
start가 호출되어야 Thread가 시작함
- start()호출하면 run() 호출, Override된 method가 호출됨.
// 1. Thread 상속
public class Test extends Thread { ... }
// 2. run method Override
@Override
public void run() {
// 3. Thread로 처리되어야할 코드(동시실행 코드)
}
// 4. 객체 생성
Test t = new Test();
// 5. start method 호출
t.start();
// Thread 클래스를 상속받아서 Thread 사용.
// 1. Thread 상속
public class UseThread extends Thread {
// 2. run() Override
@Override
public void run() {
// 3. Thread로 처리되어야할 코드 작성
for(int i =0; i< 1000; i++) {
System.out.println("run(), i ------> "+i);
}
}
public static void main(String[] args) {
// 4. 객체 생성
UseThread ut = new UseThread();
// 5. 부모클래스인 Thread가 가지고 있는 start() 호출
ut.start();
for(int i =0; i< 1000; i++) {
System.out.println("main(), i ======> "+i);
}
}
}
- 실행순서는 스케줄링에 따라 매번 달라진다.
- 항상 번갈아가며 순서대로만 실행되지 않음
// ut.start();
ut.run();
- Thread를 start()로 실행시키지 않고 run()을 실행시키면 Thread로 동작하지 않는다
- run()이 모두 끝나고 main메소드 안에 반복문이 실행된다.
Runnable interface 구현하여 Thread 사용
// 1. Runnable 구현
public class Test implements Runnable { ... }
// 2. run method Override
@Override
public void run() {
// 3. Thread로 처리되어야할 코드 작성(동시실행 코드)
}
// 4. 객체 생성
Test t = new Test();
// 5. Thread 생성, Runnable interface는 start()가 없기 때문에
Thread th = new Thread(t);
// 6. start method 호출
// has-a 관계의 클래스가 Override한 run() 호출됨
th.start();
// Runnable interface를 구현하여 Thread를 사용
// 1. Runnable 구현
public class UseRunnable implements Runnable {
// 2. run() Override
@Override
public void run() {
// 3. Thread로 동작해야하는 코드 작성
for(int i=0; i<1000; i++) {
System.out.println("run(), i -----> "+i);
}
}
public void test() {
for(int i=0; i<1000; i++) {
System.out.println("test(), i =====> "+i);
}
}
public static void main(String[] args) {
// 4. Runnable을 구현한 클래스를 객체화
UseRunnable ur = new UseRunnable();
// 5. Thread 객체와 Runnable 구현 객체를 has-a 관계로 설정
Thread th = new Thread(ur);
// 6. Thread에 있는 start() 호출
th.start(); // has-a 관계가 설정되지 않으면 Thread의 run() 호출됨.
ur.test();
}
}
-
Thread를 사용하면 끝나는 시간을 구할 수 없다.
- main method가 끝나도 다른 Thread는 계속 살아서 처리할 수 있기 때문에
public static void main(String[] args) {
long st = System.currentTimeMillis();
// 4. Runnable을 구현한 클래스를 객체화
UseRunnable ur = new UseRunnable();
// 5. Thread 객체와 Runnable 구현 객체를 has-a 관계로 설정
Thread th = new Thread(ur);
// 6. Thread에 있는 start() 호출
th.start();
ur.test();
long et = System.currentTimeMillis();
System.out.println((et-st)+"ms");
}
- Thread는 구현하는 방식을 더 많이 쓴다.
- 상속은 하나밖에 할 수 없기 때문
Sleep 사용
- Thread클래스 sleep method는 static.
// running 상태에서 block 상태로 상태 이전시키는 sleep method(생명주기)
public class UseSleep implements Runnable {
@Override
public void run() {
for(int i=0; i<10 ; i++) {
System.out.println("2 * "+i+" = "+ (2*i)); // running 상태
try {
Thread.sleep(500); // block 상태
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
UseSleep us = new UseSleep();
Thread th = new Thread(us);
th.start();
}
}
- 자바에선 main메소드도 Thread로 처리되기 때문에 Thread.sleep()은 어디서나 사용 가능하다.
// sleep을 사용한 로딩출력 예제
public class UseSleep implements Runnable {
@Override
public void run() {
System.out.print("loading");
Random r = new Random();
for(int i=0; i<15; i++) {
System.out.print(" . ");
try {
Thread.sleep(100*r.nextInt(10)+1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("finish");
}
public static void main(String[] args) {
UseSleep us = new UseSleep();
Thread th = new Thread(us);
th.start();
}
}
Thread를 사용한 1:1 채팅
- 읽는 쪽, 보내는 쪽 둘 다 Thread 처리를 해주면 됨
- 그러나 event에 대해 Thread가 작동
- 때문에 읽는 쪽에서만 Thread 사용하면 됨
- SimpleThreadChatServer class
- SimpleThreadChatClient class
Thread를 사용한 멀티 채팅
-
서버 하나에 여러 클라이언트들을 붙이는 구조
- 서버는 소켓을 List형태로 가진다.
- 클라이언트가 보내 서버가 읽어온 정보는 List안 모든 소켓에 OutputStream으로 내보낸다.
- 클라이언트가 쓴 메시지는 무조건 OutputStream으로 나가서 서버 InputStream을 통해 받게 된다.
- 대략적인 클래스 다이어그램