• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java 정리 32

27 Dec 2018

Reading time ~4 minutes

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

  • 선점형 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

  • 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);
          }
     }
}
  • 실행순서는 스케줄링에 따라 매번 달라진다.
    • 항상 번갈아가며 순서대로만 실행되지 않음

03

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

04


Runnable interface 구현하여 Thread 사용

05

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

06

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

Sleep 사용

  • Thread클래스 sleep method는 static.

07

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

08


Thread를 사용한 1:1 채팅

  • 읽는 쪽, 보내는 쪽 둘 다 Thread 처리를 해주면 됨
    • 그러나 event에 대해 Thread가 작동
    • 때문에 읽는 쪽에서만 Thread 사용하면 됨
  • SimpleThreadChatServer class
  • SimpleThreadChatClient class

09


Thread를 사용한 멀티 채팅

10

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

11



Java