---
title: "Java 정리 32"
date: "2018-12-27"
tags: ["Java"]
status: archived
verified: false
---

## Java 정리 32 - Thread

---

### Thread

* **동시에 일을 처리해야할 때 사용**
    * **동시에 여러 method를 호출가능**
* **Thread는 작은 process**
    * CPU가 한번에 처리하는 일의 단위
        * 사용자가 실행하는 프로그램
        * OS가 운영하기 위해 실행하는 프로그램
    * 메모리에 큰 프로세스를 올리는게 아니라 여러프로세스들의 조각(Thread)를 올려 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 생명주기

![01](/md/legacy-assets/java/32/01.webp)


* 선점형 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 사용

![02](/md/legacy-assets/java/32/02.webp)

* **start가 호출되어야 Thread가 시작함**
    * start()호출하면 run() 호출, Override된 method가 호출됨.

```java
// 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();
```

```java
// 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);
          }
     }
}
```

* 실행순서는 스케줄링에 따라 매번 달라진다.
    * 항상 번갈아가며 순서대로만 실행되지 않음

![03](/md/legacy-assets/java/32/03.webp)

```java
// ut.start(); 
ut.run();
```

* Thread를 start()로 실행시키지 않고 run()을 실행시키면 Thread로 동작하지 않는다
    * run()이 모두 끝나고 main메소드 안에 반복문이 실행된다.

![04](/md/legacy-assets/java/32/04.webp)

---

### Runnable interface 구현하여 Thread 사용

![05](/md/legacy-assets/java/32/05.webp)

```java
// 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();
```

```java
// 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는 계속 살아서 처리할 수 있기 때문에

```java
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");
}
```

![06](/md/legacy-assets/java/32/06.webp)


* Thread는 구현하는 방식을 더 많이 쓴다.
    * 상속은 하나밖에 할 수 없기 때문

---

### Sleep 사용

* Thread클래스 **sleep method는 static.**

![07](/md/legacy-assets/java/32/07.webp)

```java
// 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()은 어디서나 사용 가능하다.

```java
// 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();
     }
}
```

![08](/md/legacy-assets/java/32/08.webp)

---

### Thread를 사용한 1:1 채팅

* 읽는 쪽, 보내는 쪽 둘 다 Thread 처리를 해주면 됨
    * 그러나 event에 대해 Thread가 작동
    * 때문에 **읽는 쪽에서만 Thread 사용하면 됨**
* [SimpleThreadChatServer class](https://github.com/younggeun0/SSangYoung/blob/master/dev/workspace/sistJavaStudy2/src/date181227/SimpleThreadChatServer.java)
* [SimpleThreadChatClient class](https://github.com/younggeun0/SSangYoung/blob/master/dev/workspace/sistJavaStudy2/src/date181227/SimpleThreadChatClient.java)

![09](/md/legacy-assets/java/32/09.webp)

---

### Thread를 사용한 멀티 채팅

![10](/md/legacy-assets/java/32/10.webp)

* **서버 하나에 여러 클라이언트들을 붙이는 구조**
    * 서버는 소켓을 **List**형태로 가진다.
    * 클라이언트가 보내 서버가 읽어온 정보는 List안 모든 소켓에 OutputStream으로 내보낸다.
    * 클라이언트가 쓴 메시지는 무조건 OutputStream으로 나가서 서버 InputStream을 통해 받게 된다.
* **대략적인 클래스 다이어그램**

![11](/md/legacy-assets/java/32/11.webp)
