Notice
Recent Posts
Recent Comments
Link
«   2026/09   »
일 월 화 수 목 금 토
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Tags more
Archives
Today
Total
관리 메뉴

seaking110 님의 블로그

Thead 본문

Today I Learned

Thead

seaking110 2025. 1. 7. 13:54

 

프로세스와 스레드

  • 프로세스
    • 운영체제로부터 작업을 할당받는 작업의 단위
  • 스레드 
    • 프로세스가 할당받은 자원의 실행의 단위

 

프로세스 구조

  • Os가 프로그램 실행을 위한 프로세스를 할당 시 프로세스 안에 코드, 데이터, 메모리 영역 (stack, heap)을 할당
    • 코드 
      • 바이트 코드
    • 데이터 
      • 프로그램이 실행 중 저장 가능한 저장공간을 의미
      • 전역변수 정적변수(static), 배열 등 초기화된 데이터를 저장
    • 메모리
      • stack : 지역변수, 매개변수 리턴변수를 저장
      • Heap : 동적으로 필요한 변수를 저장하는 공간 

 

스레드의 생성

  • 실행요청이 들어오면 스레드를 만들어 명령을 처리
  • 프로세스 안에는 여러 스레드가 존재
  • 스레드는 프로세스안에 있는 자원을 공유

 

자바에서의 스레드

  • 일반 스레드와 동일하나 JVM 프로세스 위에서 실행
  • 무조건 Java Main 스레드부터 실행
  • JVM에 의해 실행

 

멀티스레드

  • 자바는 메인 스레드가 main 메서드를 실행시키면서 시작
  • main 스레드는 필요에 따라서 작업 스레드들을 생성해서 병렬로 코드를 실행 가능
  • 싱글 스레드
    • 프로세스 안에서 하나의 스레드만 실행되는 것
    • JVM의 메인 스레드가 종료 시 JVM도 같이 종료
  • 멀티 스레드
    • 자바 프로그램이 메인 스레드 외에 다른 작업 스레드를 생성
    • 장점
      • 여러개의 작업을 동시에 할  수 있어서 성능이 좋아짐
      • 스택을 제외한 모든 영역에서 메모리를 공유하여 자원을 효율적으로 사용 가능
      • 응답 스레드와 작업 스레드를 분리하여 빠르게 응답 가능 (비동기)
    • 단점
      • 프로세스의 자원을 공유하므로 서로 자원을 사용하려는 동기화 문제가 발생
      • 둘 이상의 스레드가 서로 자원을 원하는 상태가 되어 상대의 작업이 종료되기를 기다리며 모든 작업이 진행되지 못하게 되는 상태인 교착 상태 (deadlock)이 발생

 

스레드 구현 방법

  1. 스레드 클래스를 직접 상속 받고 사용
    • 스레드 클래스에서 run() 메서드를 오버라이딩하여 작업 내용 정의
    • 객체 생성 후 start() 호출 시 스레드 실행
// 스레드 클래스를 상속
public class TestThread extends Thread{
    @Override
    public void run() {
        // 실제 우리가 쓰레드에서 수행할 작업
        for (int i=0; i<100; i++){
            System.out.print("*");
        }
    }
}

// 메인 메서드
public static void main(String[] args) {
	// 객체 생성
	TestThread thread = new TestThread();
	// start 호출
    thread.start();
}

 

2. Rnnable 인터페이스를 구현해서 사용

  • Runnable 인터페이스를 직접 구현하여 작업 내용을 run() 메서드에 정의
  • Runnable 객체를 쓰레드 생성자에 매개변수로 전달하여 thread 실행
  • 다른 클래스를 상속 받을 수 있으므로 더욱 유연함
  • 작업과 스레드 실행을 분리하여 설계가 명확하고 재사용성이 높아 실무에 자주 사용
// Runnable 인터페이스 구현
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        for (int i=0; i<100; i++){
            System.out.print("$");
        }
    }
}

// 메인 메서드
public static void main(String[] args) {
	Runnable run = new TestRunnable();
	Thread thread = new Thread(run);
	thread.start();	
}

 

3. 람다식을 이용해 Runnable 객체를 만들어서 사용

  • 가장 간단하고 따로 클래스를 만들지 않아 유용
public static void main(String[] args) {
	Runnable task = () -> {
		for (int i = 0; i < 100; i++) {
			System.out.print("$");
		}
	};
	Thread thread1 = new Thread(task);
 	thread1.setName("thread1");
	thread1.start();
}

 

 

데몬 스레드

  • 보이지 않는 곳에서 실행되는 낮은 우선순위를 가진 스레드
  • 보조적인 역할을 담당
  • EX) 가비지 컬렉터
public static void main(String[] args) {
	Runnable demon = () -> {
		for (int i = 0; i < 1000000; i++) {
			System.out.println("demon");
		}
	};

    Thread thread = new Thread(demon);
    // 스레드를 데몬 스레드로 변경
    // 리소스를 적게 할당받기 때문에 느리게 발동
    thread.setDaemon(true);
    thread.start();
    for (int i = 0; i < 100; i++) {
        System.out.println("task");
    }
}

 

 

사용자 스레드

  • 보이는 곳에서 실행되는 높은 우선순위를 가진 스레드
  • 프로그램 기능을 담당
  • EX) 메인 스레드
  • JVM 사용자 스레드의 작업이 끝나면 데몬 스레드도 자동으로 종료

 

스레드의 우선순위

  • 작업의 중요도에 따라서 스레드의 우선순위를 부여
  • 최대 10 보통 5 최소 1
  • 스레드의 우선 순위는 setPriority(숫자) 로 부여
Thread thread1 = new Thread(task1);
thread1.setPriority(8);

 

 

 

스레드 그룹 

  • 스레드를 그룹으로 관리
  • 가장 큰 그룹은 system
  • 메인 스레드는 system 그룹의 하위 그룹인 main 그룹에 포함
  • 모든 스레드는 반드시 하나의 그룹에 포함
  • 스레드는 자신을 생성한 부모 스레드의 그룹과 우선순위를 상속

 

스레드 상태와 제어

  • 스레드의 객체가 생성되면 실행과 대기를 반복하며 run 메서드 수행
  • 또한 일시정지 상태도 가능
  • 일시정지 상태에서는 다시 실행 대기 상태로 넘어가야 실행 가능
상태
Enum
설명
객체생성
NEW
쓰레드 객체 생성, 아직 start() 메서드 호출 전의 상태
실행대기
RUNNABLE
실행 상태로 언제든지 갈 수 있는 상태
일시정지
WAITING
다른 쓰레드가 통지(notify) 할 때까지 기다리는 상태
일시정지
TIMED_WAITING
주어진 시간 동안 기다리는 상태
일시정지
BLOCKED
사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태
종료
TERMINATED
쓰레드의 작업이 종료된 상태

 

  • 스레드 제어

  •  sleep()
    • 현재 스레드를 지정된 시간동안 멈추게 한다.
    • 스레드 자신만 멈추게 한다!
    • sleep이라는 메서드는 interruptedException 을 throws 하고 있기 때문에 반드시 예외 처리를 해줘야한다.
    • interrupted 는 일시정지를 Runnable 상태로 만드는 코드로 sleep 중엔 runnable 상태가 되면 안되기 때문에!
  • interrupt()
    • 일시정지 상태인 스레드를 실행대기 상태로 만드는 것
    • sleep 단계에서 interrupt를 만나면 예외가 발생한다!
    • interrupted 된 상태인지 아닌지 여부
thread.isInterrupted(); // true or false

 

  • join()
    • 정해진 시간동안 지정한 스레드가 작업하는 것을 기다리는 것
    • 시간을 지정하지 않으면 지정한 스레드의 작업이 끝날 때 까지 기다림
    • join 역시 예외 처리를 해줘야한다!
  • yield()
    • 남은 시간을 다음 스레드에게 양보하고 스레드 자신은 실행 대기 상태가 되는 코드
  • synchronized()
    • 멀티스레드의 경우 여러 스레드가 한 프로세스의 자원을 공유하여 사용하기 때문에 서로에게 영향을 준다.
    • 이로 인해 장애나 버그가 발생하는데 이를 방지하는 방법이 스레드 동기화 (Synchronization)
    • 한 스레드가 진행중인 작업을 다른 스레드가 침범하지 못하도록 막는 것
    • 임계영역으로 침범을 막아야하는 코드를 설정
    • 임계영역에는 Lock을 가진 단 하나의 스레드만 출입
  • 임계 영역 지정
    • 메서드 전체를 임계영역으로 지정
public synchronized void asyncSum() {
	  코드
}
  • 특정 영역을 임계영역으로 지정
synchronized(해당 객체의 참조변수) {
		
}

 

 

  • wait() 와 notify()
    • 침범을 막은 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면 wait를 호출하여 lock을 반납
    • 추후 작업을 진행할 수 있다면 notify()를 호출하여 다시 진행

 

  • Lcok()
    •  synchronized 블럭은 같은 메서드 내에서만 걸 수 있기때문에 Lcok 클래스를 직접 사용한다
    • ReentrantLock
      • 재진입 가능한 Lock, 가장 일반적인 배타 Lock
      • 특정 조건에서 Lock을 풀고 나중에 다시 Lock을 얻어 임계영역으로 진입이 가능
      • 같은 스레드가 이미 락을 가지고 있더라도 락을 유지하며 계속 실행하기 때문에 데드락 발생 x
      • 코드 유연성을 높일 수 있음
    • ReentrantReadWriteLock
      • 읽기를 위한 Lock과 쓰기를 위한 Lock을 따로 제공
      • 읽기에는 공유적이고 쓰기에는 베타적인 Lock
    • StampedLock
      • 낙관적인 Lock으로  데이터를 변경할 때만 락을 걸고 푸는 Lock
      • 데이터 변경 시 충돌이 일어날 가능성이 적은 상황에서만 사용
      • 속도가 빠르지만 여러 스레드가 동시에 변경하는 건 쉽지 않다
  • condition
    • waiting pool 내 스레드를 구분하는 방법!
    • 특정 조건이 만족한 스레드만 깨우는것!
    • ReentrantLock 클래스와 함께 사용
    • wait와 notify 대신 await().와 signal() 을 사용

'Today I Learned' 카테고리의 다른 글

계산기 만들기  (0) 2025.01.10
모던 자바  (0) 2025.01.08
Generic  (0) 2025.01.06
예외 처리!  (2) 2025.01.03
객체지향 프로그래밍 이란?  (1) 2025.01.02