프로세스 내의 실행 흐름(작업) 단위
프로세스 & 쓰레드
하나의 작업을 동시에 수행하기 위해서쓰레드를 생성 및 실행하기 위해 필요한 클래스와 인터페이스
쓰레드의 동작은 Runnable 인터페이스의 run()쓰레드의 시작은 Thread 클래스의 start()@FunctionalInterface
public interface Runnable {
public abstract void run();
}Thread 객체를 만들고 생성자 매개변수로 Runnable 구현체를 넣어줌
start() 메소드 실행
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println("hi Runnable");
}
}
public class Basic {
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
new Thread(runnable).start();
}
}
public class ThreadEx extends Thread{
@Override
public void run() {
System.out.println("hi Thread");
}
}package week10_thread.basic;
public class Basic {
public static void main(String[] args) {
ThreadEx thread = new ThreadEx();
thread.start();
}
}
출처 : https://wisdom-and-record.tistory.com/48
쓰레드로 사용하려는 클래스가 다른 클래스의 상속을 받아야 할 경우package week10_thread.basic;
public class Basic {
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
ThreadEx thread = new ThreadEx();
// 세 줄의 실행 결과가 순서가 보장되지 않음
new Thread(runnable).start(); // hi Runnable
thread.start(); // hi Thread
System.out.println("hi main");
}
}
실행 순서가 보장되지 않음
ThreadGroup groupRunnable targetString namelong stackSize생성자로 매개변수를 받아서 처리public class ThreadEx extends Thread{
int number;
public ThreadEx(int param) {
number = param;
}
@Override
public void run() {
System.out.println("hi Thread");
System.out.println("파라미터로 넘겨받은 " + number + "을 run에서 사용");
}
}public class Basic {
public static void main(String[] args) {
ThreadEx paramThread = new ThreadEx(10);
paramThread.start();
System.out.println("hi main");
}
}
출처 : https://sujl95.tistory.com/63
쓰레드 객체가
생성되고 아직시작되지 않은 상태
쓰레드 스케줄러에 의해 쓰레드가
실행되도록 지정된 상태입니다.
쓰레드 실행
중지 상태
사용하려고 하는 리소스의 락이 해제되길 기다리는 상태쓰레드가
대기중인 상태
쓰레드가
특정 시간만큼 대기중인 상태
쓰레드가 종료된 상태
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}쓰레드가 가질 수 있는 최소 우선순위
쓰레드가 생성될 때 가지는 기본 우선순위
쓰레드가 가질 수 있는 최대 우선순위

중요한 작업을 하는 쓰레드에 더 많은 CPU 시간을 제공해 속도와 효율성을 높이기 위함임자바 프로세스를 시작하는 main() 메소드를 실행하는 쓰레드
백그라운드에서 메인 쓰레드를 보조하는 쓰레드
메인 쓰레드가 종료되면 데몬 쓰레드도 종료됨setDaemon(true)메인 쓰레드 종료 시 프로세스가 종료되게 하고 싶을 때 사용
여러 프로세스나 쓰레드가 공유 자원에 동시에 접근하려고 할 때,
접근 순서나 타이밍에 따라실행 결과가 달라질 수 있는 상황
공유 자원의 일관성을 보장하기 위해
하나의 프로세스나 스레드만진입해서실행 가능한 코드 영역
상호 배제 (Mutual Exclusion)진행 (Progress)한정된 대기 (Bounded Waiting)동기화 기법을 사용해야 함여러 프로세스나 쓰레드가 공유 자원에 동시에 접근해도
공유 자원의 일관성을 유지하는 것
동기화를 통해 공유 자원의 일관성을 유지하려면 임계 영역을 제대로 구현해야 함
다양한 동기화 기법이 존재 (쓰레드 → 프로세스 & 쓰레드를 의미함)
락 (Lock)
Busy Waiting 상태Busy Waiting뮤텍스 (Mutex)
잠금을 획득한 프로세스나 스레드만이 잠금을 해제할 수 있다는 점이 다른 동기화 기법세마포어(Semaphore)
공유 자원이 여러 개 존재하는 상황에서도 적용이 가능한 동기화 기법
임계 영역에 진입할 수 있는 쓰레드의 수를 제한하는 카운터를 사용
→ 상호 배제 X
💡세마포어의 크기가 1보다 큰 경우, 상호 배제를 보장하지는 않지만 공유 자원의 일관성은 유지할 수 있으므로 동기화 기법이라고 할 수 있음!
대기 큐를 사용하여 Busy Waiting 문제를 해결함 (이진 세마포어 제외)
→ CPU 자원을 절약
모니터(Monitor)
인터페이스를 두고, 인터페이스에 접근하기 위한 큐를 사용하는 동기화 기법Condition Variable)를 사용하여 프로세스나 스레드의 실행 순서를 제어할 수 있음cf) 뮤텍스 vs 모니터
자바에서
메소드나 블록을임계 영역으로 지정하는 키워드
monitormutex (lock)condition variable하나의 객체에 2개의 synchronized 메소드가 있는 경우 (a,b 메소드)a 메소드가 하나의 쓰레드(A)에 의해 실행 중
→ 해당 쓰레드가 객체의 lock을 획득
b 메소드를 사용하려는 다른 쓰레드(B)는 lock을 획득하지 못하므로 대기해야 함
package org.example.thread;
public class TestClass {
public synchronized void methodA() throws InterruptedException {
Thread.sleep(1000);
System.out.println("hi A");
}
public synchronized void methodB() throws InterruptedException {
Thread.sleep(1000);
System.out.println("hi B");
}
}
package org.example.thread;
public class ThreadMain {
public static void main(String[] args) {
TestClass testClass = new TestClass();
Thread threadA = new Thread(() -> {
try {
testClass.methodA();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
Thread threadB = new Thread(() -> {
try {
testClass.methodB();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
threadA.start();
threadB.start();
}
}
hi A // 1초
hi B // 2초
단일 변수에 대해원자적으로 작업을 수행하는 클래스
원자적 연산으로 cpu 레벨에서 상호 배제를 보장함CAS(Compare And Swap) 알고리즘을 사용해 값을 변경함package org.example.atomic;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicMain {
public static void main(String[] args) throws InterruptedException {
AtomicInteger atomicInt = new AtomicInteger(0);
// int normalInt = 0; 애초에 불가능. 컴파일 에러
final int[] normalInt = {0};
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
atomicInt.incrementAndGet(); // 원자적 연산
// normalInt++;
normalInt[0]++; // 비원자적 연산
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println("atomicInt: " + atomicInt); // 원하는 값: 100000
System.out.println("normalInt: " + normalInt[0]); // 원하는 값: 100000
}
}atomicInt: 100000
normalInt: 95052InterruptedException 예외가 발생함t1.isInterrupted();Thread.interrupted();
millisnanosInterruptedException (CheckedException)을 발생시킬 수 있기 때문에 예외 처리를 해줘야 함@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("hi Thread");
System.out.println("파라미터로 넘겨받은 " + number + "을 run에서 사용");
} catch (InterruptedException e) {
e.printStackTrace();
}
}프로세스가 다른 프로세스의 사용 자원을 무한정 대기하고 있는 상태
하나의 리소스는 하나의 프로세스만 사용 가능해야 함하나의 리소스를 사용하고 있으면서(Hold)리소스를 대기하고 있는(Wait) 프로세스가 있어야 함리소스를 강제로 뺏을 수 없어야 함대기하고 있는 프로세스의 형태가 순환 형태(Circular)를 이루고 있어야 함 ex) A → B → C → A데드락 발생 조건 4가지 중 1개 이상을 막아서 데드락 자체가 발생하지 않도록 하는 방법교착상태가 발생할 가능성이 있는 요청을 거절하는 방법주기적으로 검사하여 발견된 교착상태를 복구하는 방법쓰레드 상태
[운영체제] 데드락(Deadlock, 교착 상태)이란?
데드락
java monitor