언어/Java

[JAVA] 스레드

JM Lee 2024. 4. 13. 00:26
728x90
스레드란?
  • 컴퓨터 프로그램에서 실행되는 작업의 가장 작은 단위
  • 보통 하나의 프로그램은 여러 개의 스레드를 가질 수 있으며, 이러한 스레드들은 동시에 실행될 수 있음
  • 스레드를 사용하면 프로그램이 여러 작업을 동시에 처리하거나, 여러 작업을 병렬로 실행할 수 있어서 전체적인 성능을 향상시킬 수 있음
  • 스레드는 프로세스 안에서 메모리를 공유하므로, 데이터를 효율적으로 공유하고 통신할 수 있음
  • 스레드를 사용할 때는 동기화와 관련된 문제에 유의해야 하고, 이를 효과적으로 관리하기 위해 동기화 기술을 활용함

 

그렇다면 자바에서의 스레드 특징은?

 

  1. 멀티스레드 지원: 자바는 멀티스레드 프로그래밍을 지원하며, 여러 스레드를 생성하고 관리할 수 있는 강력한 기능을 제공
  2. 쓰레드 생성과 관리: 자바에서는 스레드를 생성하기 위해 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현하여 스레드를 정의할 수 있다.
  3. 스레드 우선순위: 자바는 스레드의 우선순위를 지정할 수 있으며, 높은 우선순위의 스레드가 CPU를 더 많이 할당받도록 할 수 있다.
  4. 동기화 메커니즘 제공: 자바는 synchronized 키워드를 통해 스레드 간의 동기화를 지원하며, 공유 자원에 대한 접근을 안전하게 제어할 수 있다.
  5. 스레드 상태 관리: 자바에서는 스레드의 상태를 관리하고, 스레드 간의 상호작용을 제어하기 위한 다양한 메서드를 제공한다.
  6. 스레드 풀 지원: 자바는 스레드 풀(Thread Pool)을 구현하여 스레드의 생성 및 관리를 효율적으로 수행할 수 있도록 지원한다.

 

 

자바 스레드의 생성자, 메소드

 

1) 생성자

  1. Thread( ) : 새로운 스레드 객체 할당
  2. Thread(String name) : 새로운 스레드 객체가 할당되며, 스레드 이름은 name으로 설정됨
  3. Thread(Runnable target) : Runnable target이 구현된 스레드 객체 할당
  4. Thread(Runnable target, String name) : Runnable target이 구현된 스레드 객체가 할당되면 스레드 이름은 name으로 설정됨.

 

2) 메소드

  1. void run( ) : 스레드의 실행코드가 작성되는 메소드로 사용자는 run() 메소드를 오버라이드 하여 사용해야 합니다.
  2. void start( ) : 스레드가 시작되도록 요청하는 메소드로 JVM은 해당 스레드의 run() 메소드를 호출합니다. 
  3. void interrupt( ) : 스레드를 중지 시킵니다. 
  4. void join( ) : 이 스레드가 끝날때까지 기다립니다. 
  5. void join(long millis) : 최대 millis 시간동안 이 스레드가 끝날때까지 기다립니다.
  6. static void sleep(long millis) : millis 시간동안 현재 스레드를 일시중지시킵니다. 
  7. static void yield( ) : 현재 스레드의 실행시간을 다른 스레드에게 양보합니다. 
  8. static Thread currentThread( ) : 현재 실행중인 스레드 객체의 참조값을 반환합니다. 
  9. long getId( ) : 스레드의 Id를 반환합니다. 
  10. String getName( ) : 스레드의 이름을 반환합니다. 
  11. int getPriority( ) : 스레드의 우선순위 값을 반환합니다. (우선순위 범위 : 1 ~ 10)
  12. Thread.State getState( ) : 스레드의 state 값을 반환합니다.
  13. ThreadGroup getThreadGroup( ) : 스레드가 속한 스레드 그룹을 반환합니다. 
  14. static boolean interrupted( ) : 현재 스레드의 interrupted 여부를 반환합니다. 
  15. boolean isInterrupted( ) : 이 스레드의 interrupted 여부를 반환합니다. 
  16. boolean isAlive( ) : 이 스레드가 살아있는지 여부를 반환합니다. 
  17. boolean isDaemon( ) : 이 스레드가 데몬 스레드인지 여부를 반환합니다. 
  18. void setDaemon(boolean on) : 이 스레드를 데몬 스레드로 변경합니다.  
  19. void setName(String name) : 이 스레드의 이름을 name으로 변경합니다. 
  20. void setPriority(int newPriority) : 이 스레드의 우선순위를 newPriority로 변경합니다. 
  21. String toString( ) : 이 스레드의 이름, 우선순위, 스레드그룹등의 정보를 담은 문자열을 반환합니다. 

cf) static 메소드는 해당 메소드를 호출한 스레드 자신에게 적용된다는 점에 유의해야합니다. 

 

 

자바에서 스레드 구현 방식은?

 

Runnable 인터페이스 구현

  1. Runnable 인터페이스를 구현하는 클래스 정의
  2. run() 메소드를 오버라이드하여 스레드 코드 작성
  3. Runnable 객체 생성하기
  4. Thread 객체 생성하기
  5. start() 메소드로 스레드 시작하기

Thread 클래스 상속

  1. Thread 클래스를 상속한 클래스 정의
  2. run() 메소드를 오버라이드하여 스레드 코드 작성
  3. 스레드 객체 생성하기
  4. start() 메소드로 스레드 시작하기

둘 다 Run() 메소드를 오버라이딩하는 방식이다.

 

보통은 Runnable을 사용하는 것을 권장한다.

자바는 단일 상속만을 지원하므로, 이미 다른 클래스를 상속한 경우 Runnable을 구현하여 쓰레드 기능을 추가할 수 있다.

 

코드 통해 스레드 특징 익히기

 

멀티스레드 지원

import java.io.*;
import java.net.*;

public class SimpleWebServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("Server is running on port 8080...");

        while (true) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("Client connected: " + clientSocket.getInetAddress());

            Thread thread = new Thread(() -> {
                try {
                    handleClientRequest(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }

    private static void handleClientRequest(Socket clientSocket) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

        String request = in.readLine();
        System.out.println("Request from client: " + request);

        // 웹 서버로부터 간단한 응답을 반환
        String response = "HTTP/1.1 200 OK\r\n\r\nHello, World!";
        out.write(response);
        out.flush();

        // 자원 해제
        in.close();
        out.close();
        clientSocket.close();
    }
}

 

 

쓰레드 생성과 관리

Runnable 객체를 생성하여 구현

public class SimpleThreadExample {
    public static void main(String[] args) {
        // Runnable 객체 생성
        Runnable task1 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Task 1: " + i);
                try {
                    Thread.sleep(1000); // 1초 대기
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // Runnable 객체 생성
        Runnable task2 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Task 2: " + i);
                try {
                    Thread.sleep(1000); // 1초 대기
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // 스레드 생성 및 시작
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);

        thread1.start(); // Task 1 수행 스레드 시작
        thread2.start(); // Task 2 수행 스레드 시작
    }
}

 

 

스레드 우선순위, 동기화 메커니즘 제공

동기화 매커니즘을 이해하는 코드

Synchronized를 통한 Lock 매커니즘 사용

>> 동시에 여러 스레드가 공유 자원에 안전하게 접근할 수 있도록 보장

public class SynchronizationExample {
    private static int counter = 0;

    public static void main(String[] args) {
        // 스레드 객체 생성
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                incrementCounter();
            }
        });

        // 두 스레드 시작
        thread1.start();
        thread2.start();

        // 두 스레드가 종료될 때까지 대기
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 결과 출력
        System.out.println("Final counter value: " + counter);
    }

    // 동기화 메서드를 사용하여 counter를 증가시키는 메서드
    private synchronized static void incrementCounter() {
        counter++;
    }
}

 

 

스레드 상태 관리

스레드 상태 관리는 스레드가 생성되어 실행되는 동안 그 상태가 변화하는 것을 관리하는 것을 의미한다. 자바에서는 Thread 클래스의 메서드와 상수를 사용하여 스레드의 상태를 관리할 수 있다.

스레드의 주요 상태는 다음과 같습니다:

  1. NEW: 스레드 객체가 생성되었지만 아직 start() 메서드가 호출되지 않은 상태입니다.
  2. RUNNABLE: 스레드가 실행되고 있거나 실행 대기 중인 상태입니다.
  3. BLOCKED: 스레드가 동기화 블록에 의해 대기하고 있는 상태입니다. 다른 스레드가 해당 블록을 사용 중이기 때문에 접근할 수 없습니다.
  4. WAITING: 스레드가 다른 스레드에 의해 통지될 때까지 무한정 기다리는 상태입니다. 예를 들어 wait(), join(), LockSupport.park() 등의 메서드 호출에 의해 발생합니다.
  5. TIMED_WAITING: 스레드가 일정 시간 동안 대기하고 있는 상태입니다. Thread.sleep(), Object.wait(long timeout), join(long millis) 등의 메서드 호출에 의해 발생합니다.
  6. TERMINATED: 스레드의 실행이 종료된 상태입니다. run() 메서드가 종료되었거나 stop() 메서드가 호출되어 스레드가 종료된 경우에 발생합니다.

스레드의 상태는 Thread 클래스의 getState() 메서드를 통해 얻을 수 있습니다. 또한 스레드의 상태 변화를 관찰하고 처리하기 위해 Thread 클래스의 다양한 메서드들을 사용할 수 있습니다. 예를 들어 wait(), notify(), join() 등의 메서드를 사용하여 스레드의 상태를 관리하고 동기화를 구현할 수 있습니다.

'언어 > Java' 카테고리의 다른 글

[Java] record  (0) 2024.04.12
[Java] JVM 개요  (0) 2024.04.11
Java static  (0) 2024.04.07
[Java] 추상 클래스란?  (0) 2024.04.07
Java 객체 속성 설정 관련 공부  (1) 2024.04.06