[Java] 스레드(Thread) 란?

백엔ㄷ현·2024년 10월 9일
0

1. 스레드의 기본 개념 :

자바에서 스레드는 동시에 여러 작업을 수행할 수 있게 해주는 중요한 기능이다. 스레드는 프로세스 내에서 독립적으로 실행되는 흐름을 말하며, 자바에서는 멀티스레딩을 통해 여러 스레드를 생성하고 동시에 실행할 수 있다. 자바의 모든 프로그램은 기본적으로 하나의 스레드에서 시작하며, 필요에 따라 추가 스레드를 생성하여 작업을 분리할 수 있다.

  • 프로세스 :

    운영체제가 할당하는 실행 단위로, 하나 이상의 스레드를 가질 수 있다.
  • 싱글 스레드 :

    한 번에 하나의 작업만 수행하는 방식. 하나의 작업이 끝나기 전까지 다른 작업은 시작할 수 없다.
  • 멀티 스레드 :

    여러 스레드가 동시에 실행되며, 하나의 작업이 진행되는 동안 다른 작업도 병행하여 처리할 수 있다. CPU 코어가 많을수록 더 많은 스레드가 실제 병렬로 실행될 수 있다.

2. 스레드 생성 방법 :

  • Thread 클래스 상속 :

    Thread 클래스를 상속받아 스레드를 생성할 수 있다. Thread 클래스를 상속받은 후, run() 메소드를 오버라이딩하여 스레드에서 수행할 작업을 정의한다.

    MyThread.java

    class MyThread extends Thread {
    	public void run() {
    		// 스레드가 실행 할 코드
    		for(int i = 0; i < 5; i++) {
    			System.out.println(Thread.currentThread().getName() + " - " + i);
    			
    			try {
    				Thread.sleep(1000);	// 1초 대기
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }

    Main.java

    public class Main {
    	public static void main(String[] args) {
    		MyThread thread1 = new MyThread();
    		MyThread thread2 = new MyThread();
    
    		thread1.start();	// 스레드 시작
    		thread2.start();	// 두 번째 스레드 시작
    	}
    }
    • MyThread 클래스는 Thread 클래스를 상속받아 run() 메소드를 오버라이드한다.
    • start() 메소드는 새로운 스레드를 시작하며, 내부적으로 run() 메소드가 실행된다.
    • Thread.sleep(1000) 는 스레드를 1초 동안 대기 상태로 만든다.


  • Runnable 인터페이스 구현 :

    Runnable 인터에시를 구현한 클래스를 만들고, run() 메소드를 정의하는 방법이다. 이 방법은 자바에서 다중 상속을 지원하지 않기 때문에, Thread 클래스를 상속받지 않고 다른 클래스를 상속해야 할 경우 유용하다.

    MyRunnable.java

    class MyRunnable implements Runnable {
    	public void run() {
    		for(int i = 0; i < 5; i++) {
    			System.out.println(Thread.currentThread().getName() + " - " + i);
    			
    			try {
    				Thread.sleep(1000);	// 1초 대기
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }

    Main.java

    public class Main {
    	public static void main(String[] args) {
    		Thread thread1 = new Thread(new MyRunnable());
    		Thread thread2 = new Thread(new MyRunnable());
    
    		thread1.start();
    		thread2.start();
    	}
    }

3. 스레드의 주요 메소드와 상태 :

  • start() : 스레드를 시작하며, 내부적으로 run() 메소드를 호출한다.

  • run() : 스레드가 실행할 코드를 포함하며, start() 메소드에 의해 호출된다.

  • sleep(long millis) : 지정한 시간(밀리초) 동안 스레드를 일시 정지시킨다.

  • join() : 현재 스레드가 다른 스레드의 종료를 기다리게 만든다.

  • yield() : 현재 실행 중인 스레드가 다른 스레드에게 CPU 자원을 양보하도록 요청한다.

  • interrupt() : 스레드의 대기 상태를 중단한다.

스레드의 상태는 Thread.State 를 통해 확인할 수 있다.

  • NEW : 스레드가 생성되었지만 아직 start() 메소드가 호출되지 않은 상태

  • RUNNABLE : 실행 가능한 상태. JVM이 스레드를 실행할 준비가 되어 있고, CPU에 의해 실행될 수 있는 상태.

  • BLOCKED : 스레드가 모니터 락을 얻기 위해 디기 중인 상태. 동기화된 블록이나 메소드에서 다른 스레드에 의해 락이 걸려 있을 떄 나타난다.

  • WAITING : 스레드가 다른 스레드의 작업이 완료될떄까지 대기 중인 상태.

  • TIMED_WAITING : 지정된 시간 동안 대기 중인 상태. slepp() 이나 wait(long timeout) 호출 시 발생한다.

  • TERMINATED : 스레드의 실행이 종료된 상태.

4. 스레드 간의 협력 :

스레드 간의 협력은 wait(), notify(), notifyAll() 메소드를 통해 이루어진다. 이들은 주로 동기화 블록에서 사용되며, 한 스레드가 작업을 마치고 다른 스레드를 깨우거나 일시적으로 대기 상태로 만들 수 있다.

  • 동기화 매커니즘 :

    여러 스레드가 동시에 같은 자원을 사용할 때, 데이터 불일치나 충돌을 방지하기 위해 동기화가 필요하다. 자바에서 동기화는 synchronized 키워드를 사용하여 블록이나 메소드를 동기화할 수 있다.

    Counter.java

    class Counter {
    	private int count = 0;
    
    	public synchronized void increment() {
    		count++;
    	}
    
    	public int getCount() {
    		return count;
    	}
    }

    Main.java

    public class Main {
    	public static void main(String[] args) throws InterruptedException {
    		Counter counter = new Counter();
    
    		Runnable task = () -> {
    			for (int i = 0; i < 1000; i++) {
    				counter.increment();
    			}
    		};
    
    		Thread thread1 = new Thread(task);
    		Thread thread2 = new Thread(task);
    
    		thread1.start();
    		thread2.start();
    
    		thread1.join();
    		thread2.join();
    
    		System.out.println("Final count : " + counter.getCount());
    	}
    }


예시코드에서 increment() 메소드는 synchronized 로 동기화 되어 여러 스레드가 동시에 호출하더라도 문제가 발생하지 않도록 보호되게 된다.


5. 스레드 풀 :

멀티스레드 애플리케이션에서 스레드를 너무 많이 생성하는 것은 성능에 부담이 될 수 있다. 이를 해결하기 위해 자바는 스레드 풀이라는 기능을 제공한다. 스레드 풀은 미리 스레드를 생성해 두고 필요할 때 재사용하여 효율적으로 자원을 관리한다.

Main.java

import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;

public class Main {
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newFixedThreadPool(3);	// 3개의 스레드
		
		for(int i = 0; i < 5; i++) {
			executorService.submit(() -> {
				System.out.println(Thread.currentThread().getName() + " is working");
              try {
                  Thread.sleep(1000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          });
		}

		executorService.shutdown();	// 스레드 풀 종료
	}
}



6. 스레드의 중단 :

스레드를 중단하는 것은 스레드가 동작하는 동안 종료시키는 작업을 의미한다. 자바에서 스레드를 강제로 종료하는 것은 권장되지 않는다. interrupt() 메소드를 사용하여 스레드에 인터럽트를 걸고, 스레드 내부에서 이를 처리하는 방식으로 구현하는 것이 일반적이다.

MyThread.java

class MyThread extends Thread {
	public void run() {
		try {
			for(int i = 1; i) {
				System.out.println("Thread is working : " + i);
				Thread.sleep(1000);		// 스레드가 1초 대기
			}
		} catch (InterruptedException e) {
			System.out.println("Thread was interrupted during sleep."):
		}
	}
}

Main.java

public class Main {
	public static void main(String[] args) throws InterruptedException {
		MyThread thread = new MyThread();
		thread.start();

		Thread.sleep(3000);		// 3초 후 인터럽트
		thread.interrupt();		// 스레드 중단 요청
	}
}

스레드를 공부 하면서 느낀점 :

책을 보면서 내용 요약과 예시 코드를 작성하면서 스레드의 기본 개념과 작동 방식을 알게 되었다.
하지만 완전히 이해한건 아니라 기록을 해두고 시간날때마다 가끔씩 들여다보면서 개념을 확실하게 확립해야 할 것 같다.

profile
매일매일 공부한 내용과 코드 기록하겠습니다

0개의 댓글