- 학습 목표 달성 확인 목록
- [] 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();
}