Skip to the content.

- 학습 목표 달성 확인 목록

- [] Hz의 의미를 아는가?

한번의 전기공급(1클럭)->1Hz(1초의 1클럭==1작업)을 의미한다

- [] 멀티태스킹의 개념과 원리를 이해하는가?

동시에 여러 작업을 수행하는것을 말하며

실제로는 이작업 저작업 돌아가면서 실행한다

그렇지만 동시에 실행되는것으로보인다면 그 이유는 CPU가 매우 빠르기 때문이다

- [] CPU Scheduling 이 무엇인지 설명할 수 있는가?

위에서 설명한거 처럼 돌아가면서 실행하는 것을 CPU가 분배를 해주는데 이를 의미한다.

- [] 라운드로빈 스케쥴링과 우선순위 스케쥴링을 설명할 수 있는가?

라운드 로빈:시간을 일정하게 쪼개서 프로세스에 순차적으로 분배

이는 윈도우OS에서 사용하는 기법이다.

우선순위:우선 순위가 높은 프로세스에게 더 많이 분배하준다

주로 유닉스,리눅스 계열에서 보이는 기법이다

- [] 컨텍스트 수위칭을 설명할 수 있는가?

CPU가 여러 프로세스를 실행하는데

예를 들어 P1 P2 P3 P4 라고 프로세스들이 있다고 치자

스케쥴링에 따라 P1 -> P2 -> P3 -> P4 ->P1…

골고루 돌아가면서 실행하는데 다른 프로세스에 넘어갈때 마다

책갈피처럼 프로세스의 실행정보를 저장하고 다시 자기 차례가 오면 저장한 정보를 읽어오는 작업을 

context switching 이라고 부른다

- [] 멀티 태스킹 구현 방법에서 멀티 프로세싱과 멀티 스레딩의 차이를 이해하는가?

멀티 프로세싱:예전 방식인 복제 방식이며 말그대로 프로세스를 복제하여 스레드들을 실행시킨다

하지만 문제점이 있는데 이때 메모리도 똑같이 복제한다->메모리 낭비가 심하다->무거워진다 

멀티 스레딩:멀티 프로세싱의 단점을 보완한 것인데 프로세스의 Heap과 코드를 공유한다->메모리 사용률이 현저히 낮아진다->가벼워진다

- [] JVM을 실행할 때 기본으로 생성되는 스레드를 아는가?

메인스레드이다

출처:엄진영 강사님

- [] JVM이 기본으로 생성하는 스레드 및 스레드 그룹을 조회할 수 있는가?

public static void main(String[] args) {
    // JVM은 여러 개의 스레드를 실행한다.
    // main() 호출도 별도의 스레드가 실행한다.
    // 확인해보자!

    // 이 순간 실행 중인 흐름이 무엇인지 알고 싶다면?
    Thread t = Thread.currentThread();

    System.out.println("실행 흐름명 = " + t.getName());

    // 실행 흐름을 전문적인 용어로 "Thread(실 타래)"라 부른다.
    // JVM이 실행될 때 main() 메서드를 호출하는 실행 흐름(스레드)의 이름은 "main"이다.
  }
  
  //결과
  실행 흐름명 = main
public static void main(String[] args) {
    Thread main = Thread.currentThread();
    ThreadGroup mainGroup = main.getThreadGroup();

    // 스레드 그룹에 소속된 스레드 목록을 알고 싶다면?
    Thread[] arr = new Thread[100];
    int count = mainGroup.enumerate(arr, false);
    // 두 번째 파라미터 값을 false로 지정하면,
    // 하위 그룹에 소속된 스레드들은 제외한다.
    // 즉, 현재 그룹에 소속된 스레드 목록만 가져오라는 뜻!

    System.out.println("main 그룹에 소속된 스레드들:");
    for (int i = 0; i < count; i++)
      System.out.println("   => " + arr[i].getName());
  }
  
  //결과
  main 그룹에 소속된 스레드들:
   => main
public static void main(String[] args) {
    // JVM의 최상위 스레드 그룹인 system의 계층도 출력하기
    Thread mainThread = Thread.currentThread();
    ThreadGroup mainGroup = mainThread.getThreadGroup();
    ThreadGroup systemGroup = mainGroup.getParent();

    printThreads(systemGroup, "");
  }

  static void printThreads(ThreadGroup tg, String indent) {
    System.out.println(indent + tg.getName() + "(TG)");

    // 현재 스레드 그룹에 소속된 스레드들 출력하기
    Thread[] threads = new Thread[10];
    int size = tg.enumerate(threads, false);
    for (int i = 0; i < size; i++) {
      System.out.println(indent + "  ==> " + threads[i].getName() + "(T)");
    }

    // 현재 스레드 그룹에 소속된 하위 스레드 그룹들 출력하기
    ThreadGroup[] groups = new ThreadGroup[10];
    size = tg.enumerate(groups, false);
    for (int i = 0; i < size; i++) {
      printThreads(groups[i], indent + "  ");
    }
  }
  
  //결과
  system(TG)
  ==> Reference Handler(T)
  ==> Finalizer(T)
  ==> Signal Dispatcher(T)
  main(TG)
    ==> main(T)
  InnocuousThreadGroup(TG)
    ==> Common-Cleaner(T)

- [] 스레드와 JVM 스택 메모리의 관계를 아는가?

- [] 스레드를 만드는 방법을 아는가?

상속받아 스레드 구현

public static void main(String[] args) {

    // 1) Thread 클래스를 상속 받아 정의하기
    // => 구현하기 편하다.
    // => 그런데 다중 상속이 불가능하기 때문에 다른 클래스를 상속 받을 수 없다.
    // => 즉 MyThread가 다른 클래스를 상속 받으면서 스레드가 될 순 없다.
    //
    class MyThread extends Thread {
      // 기존의 스레드에서 분리해서 새 스레드에서 실행하고픈 코드가 있다면,
      // run()을 재정의하여 그 메서드에 해당 코드를 두어라!
      @Override
      public void run() {
        // 별도로 분리해서 병행으로 실행할 코드를 두는 곳!
        for (int i = 0; i < 1000; i++) {
          System.out.println("===> " + i);
        }
      }
    }

    // 스레드 실행
    // => Thread의 서브 클래스는 그냥 인스턴스를 만들어 start()를 호출한다.
    MyThread t = new MyThread();
    t.start(); // 실행 흐름을 분리한 후 즉시 리턴한다. 비동기로 동작한다.

    // "main" 스레드는 MyThread와 상관없이 병행하여 실행한다.
    for (int i = 0; i < 1000; i++) {
      System.out.println(">>>> " + i);
    }
  }
익명클래스로 구현하기
public static void main(String[] args) {
    new Thread() {
      @Override
      public void run() {
        for (int i = 0; i < 1000; i++) {
          System.out.println("===> " + i);
        }
      }
    }.start();

    for (int i = 0; i < 1000; i++) {
      System.out.println(">>>> " + i);
    }

  }

Runnable 인터페이스 구현

public static void main(String[] args) {

    // 2) Runnable 인터페이스를 구현하기
    // => 실무에서 스레드를 만들 때 많이 사용한다.
    // => 인터페이스를 구현하는 것이기 때문에 다른 클래스를 상속 받을 수 있다.
    // => 직접적으로 스레드가 아니기 때문에 실행할 때는 Thread의 도움을 받아야 한다.
    class MyRunnable implements Runnable {
      @Override
      public void run() {
        // 별도로 분리해서 병행으로 실행할 코드를 두는 곳!
        for (int i = 0; i < 1000; i++) {
          System.out.println("===> " + i);
        }
      }
    }

    // 스레드 실행하기
    // => Runnable 구현체를 Thread 객체에 실어서 실행한다.
    // => start()를 호출하여 기존 스레드에서 분리하여 스레드를 실행시킨다.
    Thread t = new Thread(new MyRunnable());
    t.start(); // 실행 흐름을 분리한 후 즉시 리턴한다.

    // "main" 스레드는 Thread와 상관없이 병행하여 실행한다.
    for (int i = 0; i < 1000; i++) {
      System.out.println(">>>> " + i);
    }
  }
익명 클래스로 구현
public static void main(String[] args) {

    new Thread(new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i < 1000; i++) {
          System.out.println("===> " + i);
        }
      }
    }).start();

    for (int i = 0; i < 1000; i++) {
      System.out.println(">>>> " + i);
    }

  }
람다로 구현
public class Exam0230 {

  public static void main(String[] args) {

    new Thread(() -> {
      for (int i = 0; i < 1000; i++) {
        System.out.println("===> " + i);
      }
    }).start();

    for (int i = 0; i < 1000; i++) {
      System.out.println(">>>> " + i);
    }

  }

- [] 스레드의 생명주기를 설명할 수 있는가?

- [] sleep()/timeout, join() 메서드를 사용할 수 있는가?

public static void main(String[] args) throws Exception {
    System.out.println("스레드 실행 전");

    new Thread() {
      @Override
      public void run() {
        System.out.println("Hello!");
      }
    }.start();

    // 3초 동안 not runnable 상태로 만든다.
    // => 즉 3초 동안 CPU가 놀고 있더라도 CPU를 사용하지 않는다.
    // => 3초가 지나면(timeout) 다시 "main" 스레드는 CPU를 받아 실행할 수 있다.
    // => sleep()을 호출하면 그 순간에 실행하는 스레드를 잠들게 한다.
    Thread.sleep(3000); // milliseconds

    System.out.println("스레드 실행 후");
  }
  public static void main(String[] args) throws Exception {
    System.out.println("스레드 실행 전");

    Thread t = new Thread(() -> {
      for (int i = 0; i < 1000; i++) {
        System.out.println("===> " + i);
      }
    });

    t.start(); // 스레드를 생성하고 시작시킨다.

    //    t.join(); // t 스레드가 종료될 때까지 "main" 스레드는 기다린다.

    // 즉 t 스레드가 종료된 후 다음 코드를 실행한다.
    System.out.println("스레드 종료 후");

    // 스레드 종료 후 다시 시작시킨다면?
    // => IllegalThreadStateException 발생!
    // => 즉 종료된 스레드는 다시 running 할 수 없다.
    //t.start();

  }