Java 스레드

Jinny·2022년 1월 18일
0

TIL

목록 보기
19/28
post-thumbnail

스레드

💡 프로세스 내에서 할당된 자원을 이용해 실제 작업을 수행하는 작업 단위 모든 프로세스는 하나 이상의 스레드를 가지며 각각 독립적인 작업단위를 가짐

싱글 스레드 vs 멀티 스레드

  • 싱글 스레드
    메인 스레드 하나만 가지고 작업을 처리 → 한 작업씩 차례대로 처리함.

  • 멀티 스레드
    메인 스레드 외의 추가적인 스레드를 이용하여 병렬적으로 작업 처리.


멀티 스레드 장점 / 단점

장점

  • 자원을 보다 효율적으로 사용 가능
  • 사용자의 대한 응답성 향상
  • 애플리케이션의 응답성 향상
  • 작어빙 분리되어 코드가 간결해짐
  • CPU 사용률 향상

단점

  • 동기화(Synchronization)에 주의
  • 교착상태(dead-lock)가 발생하지 않도록 주의

멀티스레드 예시

package test.thread1;

public class ThreadA /*extends Thread*/ implements Runnable {
	@Override
	public void run() {
		// 다른 스레드 클래스와 동시 처리하고 싶은 내용을 코드로 작성함
		// 다른 스레드가 동작하지 않는 구간에서 이 부분 처리함. 그냥 보기에는 동시에 처리되는 것처럼 보일 수 있음.
		for(int i = 1; i <= 10; i++) {
			System.out.println("★");
			try {
				Thread.sleep(100); // sleep을 안 써주면 A 동작 다한 후 B 동작함.
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
package test.thread1;

public class ThreadB /*extends Thread*/ implements Runnable {
	@Override
	public void run() {
		for(int i = 1; i <= 10; i++) {
			System.out.println(i);
			try {
				Thread.sleep(100); // sleep을 안 써주면 A 동작 다한 후 B 동작함.
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
package test.thread1;

public class ThreadC extends Thread {
	@Override
	public void run() {
		super.run();
		for(int i = 1; i <= 10; i++) {
			System.out.println("♥");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
package test.thread1;

public class TestThread {
	public static void main(String[] args) {
	
		// Runnable 인터페이스 상속 시
		// 1. 스레드 생성
		Thread t1 = new Thread(new ThreadA());
		Thread t2 = new Thread(new ThreadB());

		// Thread extends 시
		ThreadC t3 = new ThreadC();
		
		// 1-2. 우선순위 설정
		t2.setPriority(Thread.MAX_PRIORITY);
		
		// 2-1. t1 스레드 start();   --> 실행(run()) 대기
		t1.start();
		// 2-2. t2 스레드 start();   --> 실행(run()) 대기
		t2.start();
		t3.start();
	}
}

// 실행결과
1
♥
★
2
♥
★
3
★
♥
4
♥
★
★
♥
567
♥
★
8
★
♥
9
♥
★
10
★
♥

스레드 스케줄링

💡 스레드 개수가 코어의 수보다 많을 경우 스레드를 어떤 순서로 동시성을 실행할 것인가 결정하는 것

우선 순위 방식(Priority)

  • 우선 순위가 높은 스레드가 작업 시간을 더 많이 가지도록 하는 방식
  • 스레드에 1~10까지 우선 순위 번호 부여 가능(번호가 높을수록 우선 순위 높음)
  • 스레드 생성 시 우선 순위 기본값은 5

순환 할당 방식(Round-Robbin)

  • 시간 할당량(Time Slice)를 정하여 하나의 스레드를 정해진 시간만큼 실행시키는 방식
  • JVM에 의해 정해짐

스레드 컨트롤

💡 실행 중인 스레드의 상태를 제어하기 위한 것으로 효율적이고 정교한 스케줄링을 위한 스레드 상태를 제어하는 기능

📌 sleep() - 일시 정지

💡 sleep(long millis) - ms만큼 일시 정지
   sleep(long millis, int nanos) - ms + nanos만큼 일시 정지

지정된 시간동안 스레드를 멈추게하여 일시 정지상태가 된다.
지정된 시간이 다 되거나 interrupt()가 호출되면 InterruptedException이 발생되어 실행 대기 상태가 되기 때문에 항상 예외 처리(try - catch)를 해주어야 한다.

📌 interrupt() - 작업 취소

💡 void interrupt() - interrupted 상태를 true로 변경
   boolean isInterrupted() - interrupted상태를 반환
   static boolean interrupted() - interrupted상태를 반환하고 false로 변경

진행 중인 스레드의 작업이 끝나기 전에 취소시킨다.
interrupt() 메소드는 스레드의 작업을 일시정지시키며 종료시키지는 않는다.
Thread 객체의 interrupted상태를 변경시킨다.

📌 suspend(), resume(), stop() - 실행 제어

💡 suspend() - 스레드 일시 정지
resume() - suspend()로 일시 정지된 스레드를 실행 대기 상태로 변경
stop() - 스레드 종료

세 가지 메소드 모두 교착상태를 일으키기 쉬워 사용이 권장되지 않는다.

📌 yield() - 작업 양보

💡 yield() - 작업 시간 양보

스레드가 자신에게 주어진 작업 시간을 다음 차례의 스레드에게 양보한다.
yield() 메소드를 수행하면 실행 대기 상태가 된다.
프로그램의 응답성을 높이고 효율적인 실행을 위해 사용한다.

📌 join() - 다른 스레드의 종료 기다림

💡 join() - 다른 스레드의 작업을 마칠 때까지 일시 정지
join(long millis) - 다른 스레드의 작업 종료 또는 ms 만큼 일시 정지
join(long millis, int nanos) - 다른 스레드의 작업 종료 또는 ms+nanos만큼 일시정지

스레드가 작업을 잠시 멈추고 다른 스레드의 작업이 수행하도록 할 때 사용한다.
interrupt()에 의해 대기 상태에서 벗어날 수 있고 InterruptException이 발생하므로 예외 처리(try - catch)를 해줘야 한다.


동기화(Synchronized)

💡 한 번에 한 개의 스레드만 프로세스 공유 자원(객체)에 접근할 수 있도록 락(Lock)을 걸어 다른 스레드가 진행 중인 작업에 간섭하지 못 하도록 하는 것

동기화 메소드와 동기화 블록

일반 메소드는 스레드1, 2 동시에 공유 가능하다.
하지만 동기화는 스레드1이 끝나야 스레드2가 실행이 가능하다.

// 동기화 메소드
public synchronized void method() {
	// 한 개의 스레드만 실행할 수 있음.
}

// 동기화 블록
public void method1() {
	// 여러 스레드 실행할 수 있음

	synchronized (공유객체) {
		// 한 개의 스레드만 실행할 수 있음.
	}
	// 여러 스레드 실행할 수 있음
}

📌 wait() - 스레드 대기

💡 void wait() - 락을 반납하고 통지를 대기
void wait(long millis) - 락을 반납하고 ms만큼 대기
void wait(long millis, int nanos) - 락을 반납하고 ms+nanos만큼 대기

동기화된 블록 안에서 다른 스레드가 이 객체의 notify(), notifyAll()을 호출하거나 지정된 시간이 지날 때까지 현재의 스레드를 대기시킨다.

📌 notify() - 스레드를 깨움

💡 void notify() - 객체 대기 풀의 임의의 스레드에 통지

동기화된 블록 안에서 호출한 객체 내부에 대기중인 스레드를 깨운다.
여러 스레드가 있을 경우 임의의 스레드 하나에만 통보

📌 notifyAll() - 모든 스레드를 깨움

💡 void notifyAll() - 객체 대기 풀의 모든 스레드에 통지

동기화된 블록 안에서 호출한 객체 내부에 대기중인 모든 스레드를 깨운다.
하지만, lock은 하나의 스레드만 얻을 수 있다.

profile
코린이

0개의 댓글