728x90

프로세스

실행 중인 프로그램을 의미하며, 자원(메모리, CPU등)과 쓰레드로 구성

쓰레드

프로세스 내에서 실제 작업을 수행하는 역할

모든 프로세스는 최소 한 개 이상의 쓰레드를 가지고 있음

멀티 쓰레드의 장점

  • 시스템 자원의 효율적인 사용
  • 사용자에 대한 응답성 향상
  • 작업이 분리되어 코드가 간결해짐

멀티 쓰레드의 단점

  • 동기화에 주의해야 함
  • 교착상태가 발생하지 않도록 주의
  • 각 쓰레드가 효율적으로 고르게 실행되게 고려해야 함
  • 즉, 효율은 좋으나 개발을 하는데 있어서 쉽지 않음

쓰레드의 구현 및 실행

// Thread 클래스를 상속 받아 구현하는 방법
class MyThread extends Thread {
	public void run() {
    	//Thread 클래스의 run 메소드를 오버라이딩 하여 사용
    }
}

// 쓰레드 사용
MyThread mt = new MyThread(); // 쓰레드 생성
mt.start(); // 쓰레드 실행

// Runnable 인터페이스를 구현하는 방법
class MyThread implements Runnable {
	public void run() {
    	//run 메소드를 구현하여 사용
    }
}

// 쓰레드 사용
Runnable r = new MyThread();
Thread t = new Thread(r); // Thread t = new Thread(new MyThread()) 과 같음
t.start();

자바는 단일 상속만 가능하기 때문에 Thread 클래스를 상속 받아 구현하는 방법보다는

Runnable 인터페이스를 구현해서 사용하는 것이 더 효과적

start()

호출 스택을 하나 추가한 후에 스택에 run 메소드를 올리고 삭제되는 메소드로

start 메소드를 사용했다고 쓰레드가 바로 실행되는 것이 아니라

시작대기상태에 들어간 후에 운영체제의 스케줄러가 쓰레드를 동작시킴

main 쓰레드

main 메소드의 코드를 수행하는 쓰레드로 사용자 쓰레드에 속함

프로그램은 사용자 쓰레드가 하나도 없을 때 종료되므로

기존에는 메인 쓰레드만 사용하여 메인 쓰레드의 작업이 끝나면 종료됫지만

start 메소드를 통해 사용자 쓰레드를 추가로 실행하면

메인 쓰레드가 끝나도 프로그램이 종료되지 않음

싱글 쓰레드와 멀티 쓰레드의 실행속도 차이

멀티 쓰레드는 동시에 여러 작업을 수행할 수 있지만 싱글 쓰레드보다 실행속도 차이가 조금 더 걸림

멀티 쓰레드는 컨텍스트 스위칭에 걸리는 시간이 있기 때문

쓰레드의 I/O 블락킹

싱글 쓰레드에서는 입출력시 작업이 중단되기 때문에 다른 작업들이 진행되지 않지만

멀티 쓰레드는 입출력이 진행되는 한쪽 쓰레드가 멈춰 있어도 진행되기 때문에 효율적임

쓰레드의 우선순위

쓰레드는 작업의 중요도에 따라 우선순위를 다르게 지정하여 특정 쓰레드가 더 많은 작업시간을 갖게 할 수 있음

하지만, 운영체제 스케줄러에서 정하는 우선순위가 우선이기 때문에 큰 영향이 있지는 않음

// 쓰레드의 우선순위 지정 (1 ~ 10으로만 지정 가능)
// 기본 우선순위는 5로 지정되어 있음
void setPriority(int newPriority)

// 우선순위 확인
getPriority()

쓰레드 그룹

  • 서로 관련된 쓰레드를 그룹으로 묶어서 다루기 위한 것
  • 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되야 함
  • 그룹을 정하지 않고 생성한 쓰레드는 기본적으로 main 쓰레드 그룹에 속함
  • 자신을 생성한 쓰레드의 그룹과 우선순위를 상속 받음
// 생성자
ThreadGroup(String name) // 새로운 쓰레드 그룹 생성
ThreadGroup(ThreadGroup parent, String name) // 부모 쓰레드 그룹에 포함되는 새로운 그룹 생성
Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)

// 쓰레드 그룹 확인
getThreadGroup()

// 쓰레드 그룹의 메소드
activeCount() // 그룹에 포함된 활성상태의 쓰레드 수 반환
activeGroupCount() // 그룹에 포함된 활성상태의 쓰레드 그룹 수 반환
chcekAccess() // 현재 실행중인 쓰레드가 쓰레드 그룹을 변경할 권한이 있는지 확인
destroy() // 비어있는 경우에만 쓰레드 그룹과 하위 그룹까지 모두 삭제
enumerate() //
getMaxPriority()
getName()
getParent()
interrupt()
isDestroyed()
list()
parentOf()
setMaxPriority()

데몬 쓰레드

  • 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 데몬 쓰레드
  • 일반 쓰레드가 종료된다면 자동적으로 데몬 쓰레드는 모두 종료됨
  • 가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용
// 데몬 쓰레드 지정
// 해당 메소드는 반드시 start 메소드를 호출하기 전에 실행해야 함
setDaemon(true)
// 데몬 쓰레드인지 확인
isDaemon()

// 무한루프와 조건문을 이용해 실행 후 대기하다 특정조건 만족시
// 작업을 수행하고 수행 후 다시 대기하도록 작성
public void run() {
	while(true) {
        try {
            Thread.sleep(3 * 1000);
        } catch(InterruptedException e) { }

        if(autoSave) {
            autoSave();
        }
    }
}

쓰레드의 상태

NEW : 쓰레드가 생성되고 아직 start 메소드가 호출되지 않은 상태

RUNNABLE : 실행 중 또는 가능한 상태

BLOCKED : 동기화블럭에 의해 일시정지된 상태 또는 LOCK이 풀릴 때까지 기다리는 상태

WAITING, TIMED_WAITING : 쓰레드가 종료 상태는 아니지만 실행가능하지 않은 상태

TERMINATED : 쓰레드의 작업이 종료된 상태

쓰레드의 실행제어

쓰레드의 실행을 제어할 수 있는 메소드를 활용해 효율적인 프로그램 작성 가능

// 지정 시간만큼 쓰레드를 일시정지 시키고 자동적으로 다시 실행대기상태 진입
// Exception 클래스의 자손이라 예외처리를 통해 사용해야 함
// static 메소드라 자기 자신만 멈추게 하는 것이 가능
try {
	sleep() 
} catch (InterruptedException e) {} // 예외가 발생하면 깨어남

// 지정된 시간동안 다른 쓰레드가 작업하는 것을 기다리게함
// 시간 미지정시 작업이 모두 끝날 때까지 기다림
// sleep과 마찬가지로 예외처리 해야 함
try {
	join() 
} catch (InterruptedException e) {}

// sleep이나 join을 깨워서 실행대기상태로 진입
interrupt() // 쓰레드의 interrupted 상태를 true로 변경
isInterrupted() //  쓰레드의 interrupted 상태를 반환
interrupted() // 쓰레드의 상태를 알려주고 false로 초기화

@deprecated
// 아래의 3개는 교착상태(dead-lock)가 생길 수 있어 사용을 권장하지 않음
// 사용해야한다면 오버라이딩해서 사용하는 것을 권장
// intrrupt, notify, wait 메소드들도 대체해서 사용하는 것이 가장 좋음
stop() // 쓰레드를 즉시 종료
suspend() // 쓰레드를 일시정지
resume() // suspend에 의해 일시정지 상태인 쓰레드를 실행대기상태로 진입

// 자신에게 주어진 실행시간을 다른 쓰레드에게 양보 후 실행대기상태 진입
// static 메소드라 자기 자신의 쓰레드에만 적용가능
// yield와 interrupt 메소드를 잘 활용하면 응답성과 효율을 높일 수 있음
yield()

쓰레드의 동기화

  • 멀티 쓰레드는 메모리를 공유하기 때문에 다른 쓰레드의 작업에 영향을 미칠 수 있기 때문에 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 동기화를 해줘야 함
  • 동기화를 하기 위해선 간섭받지 말아야 하는 문장들을 임계 영역으로 설정
  • 임계 영역은 락을 얻은 단 하나의 쓰레드만 출입가능(객체마다 락 1개만 가짐)
  • 임계 영역은 프로그램의 성능에 영향을 미치기 때문에 가능한 최소화 하는 것이 좋음 
// synchronized를 이용한 동기화
자바제어자 synchronized 반환타입 메소드명() { // 해당 메소드 전체를 임계 영역으로 지정
	메소드내용
}

자바제어자 synchronized 반환타입 메소드명() {
	synchronized(객체의 참조변수) { // 영역의 일부만 임계 영역을 지정
		코드작성
	}
}

wait / notify

동기화의 효율을 높이기 위해 사용하는 메소드

Object 클래스에 정의되어 있으며, 동기화 블록 내에서만 사용가능

여러 쓰레드가 공유하는 부분은 동기화해주는 것이 좋음

// 객체의 락을 풀고 쓰레드를 해당 객체의 waiting pool에 넣음
wait()

// waiting pool에서 대기중인 쓰레드 중의 하나를 랜덤으로(순서X) 깨움
notify()

// waiting pool에서 대기중인 모든 쓰레드를 깨움(일반적으로 많이 사용)
notifyAll()

Lock / Condition

나중에 찾아보기

'Java > Notion' 카테고리의 다른 글

Stream  (1) 2023.05.03
Lambda  (0) 2023.05.03
애너테이션  (0) 2023.05.02
열거형(Enum)  (0) 2023.05.01
Generics  (0) 2023.05.01

+ Recent posts