본문 바로가기

Java/기본 개념

스레드(Thread)

▶ 스레드 (Thread)

1) 프로세스 (Process)

  • 간단한 의미로 실행 중인 프로그램
  • 프로세스는 프로그램이 실행될 때마다 개별적으로 생성
  • 하나의 프로세스는 프로그램 수행에 필요한
    데이터와 메모리 등의 할당 받은 자원 & 하나 이상의 스레드로 구성됨

2) 스레드 (Thread)

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

3) 메인 스레드

  • 모든 자바 프로그램은 메인 스레드가 메인 메소드( main( ) )를 실행하며 시작
  • main( ) 메소드의 첫 코드부터 아래로 순차적으로 실행되고, return을 만나면 종료됨
  • 필요에 의해 여러 작업 스레드를 만들어서 병렬 코드를 실행 가능(멀티 스레드를 이용한 멀티 태스킹)

4) 프로세스 종료

  • 싱글 스레드인 경우, 메인 스레드가 종료되면 프로세스 종료 O
  • 멀티 스레드인 경우, 실행 중인 스레드가 하나라도 있다면 프로세스 종료 X

5) 멀티 프로세스 VS 멀티 스레드

  • 멀티 프로세스 : 각각의 프로세스를 독립적으로 실행하는 것
  • 멀티 스레드 : 하나의 프로세스 내에서 여러 스레드가 동시에 작업을 수행하는 것

6) 싱글 스레드 VS 멀티 스레드

  • 싱글 스레드 : 메인 스레드 하나만으로 작업 처리 → 한 작업씩 차례대로 처리
  • 멀티 스레드 : 메인 스레드 외 추가적인 스레드를 이용하여 병렬적으로 작업 처리

7) 멀티 스레드의 장점

  1. 자원을 보다 효율적으로 사용
  2. 사용자에 대한 응답성 향상
  3. 어플리케이션의 응답성 향상
  4. 작업이 분리되어 코드 간결해짐
  5. CPU 사용률 향상

8) 멀티 스레드의 단점

  • 동기화(Synchronization)에 주의
  • 교착상태(deda-lock)가 발생하지 않도록 주의
  • 프로그래밍 시 고려할 사항이 많음

9) 스레드 생성

  • Thread 클래스 상속 받기
    - run( ) 메소드가 추상 메소드가 아니라서 강제 오버라이딩 X
public class TestThread1 extends Thread{

// Thread.currentThread() : 현재 실행 중인 스레드를 반환
// Thread.currentThread().getName() : 현재 실행 중인 스레드의 이름 반환

   @Override
   public void run() {
   // run() 메소드 : 스레드가 생성되어 실행( start() ) 시 수행될 구문을 작성하는 메소드
      System.out.println(Thread.currentThread().getName()+" 실행");

      for(int i=0; i<10; i++) {
            System.out.println("0번 스레드 "+i);
      }
   }
}

public class TestRun {
   public static void main(String[] args) {
   // 방법 1. Thread 클래스 상속
      TestThread1 test1 = new TestThread1();
      test1.start(); // 스레드 실행
   }
}
  • Runnable 인터페이스 구현
    - run( ) 메소드 오버라이딩이 강제됨
public class TestThread2 implements Runnable{

   @Override
   public void run() {
      System.out.println(Thread.currentThread().getName()+" 실행");
		
      for(int i=0; i<10; i++) {
          System.out.println("1번 스레드 "+i);
      }
   }
}

public class TestRun {
   public static void main(String[] args) {
   // 방법 2. Runnable 인터페이스 상속
   // new Thread( Runnable target ) : 
   // - Runnable 인터페이스를 상속 받은 객체를 매개변수에 추가
      Thread test2 = new Thread( new TestThread2());
      test2.start();
   }
}

10) run( ) / start( ) 메소드

  • run( ) 호출

  • start( ) 호출

11) 스레드 컨트롤

  • 실행 중인 스레드 상태를 제어하기 위한 것
  • 효율적이고 정교한 스케쥴링을 위한 스레드 상태를 제어하는 기능
  • 주로 sleep( ), interrupt( ) 위주로 사용됨

  • sleep( ) 예제
// 스레드 생성(Runnable 인터페이스 상속)
public class SleepThread1 implements Runnable{
   @Override
   public void run() {
      for(int i=1; i<=10; i++) {
          // Thread.sleep(long millis)
          // - 현재 스레드를 지정된 시간만큼 일시정지
          // - 시간은 1/1000초 단위(ms)
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
          System.out.println(i+"초");
      }
   }
}


public class MyClock implements Runnable{
   @Override
   public void run() {
   // 00시 00분 00초 형식으로 시계 만들기
   // SimpleDateFormat : 간단하게 날짜/시간 형식을 지정하는 객체
   SimpleDateFormat sdf = new SimpleDateFormat("HH시 mm분 ss초");
   // HH -> 시 (24시간) / mm -> 분 / ss -> 초
		
      while(true) {
           Date date = new Date(); // 현재 시간이 객체로 생성되며 저장
			
           System.out.println(sdf.format(date));
			
           try {
              Thread.sleep(1000); // 시간 출력 후 1초 일시정지
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
      }
   }
}
  •  + interrupt( ) 예제
  • interrupt( ) : 스레드의 동작을 방해해서 멈추게 함
  • void interrupt( )
    - 현재 스레드의 interrupted 필드값을 true로 변경
    - interrupted 필드 == false → 작업 진행
    - interrupted 필드 == true  → 작업 멈춤
  • boolean isInterrupted( )
    - 현재 스레드의 interrupted 필드값을 반환 (getter)
  • boolean Thread.interrupted( )
    - 현재 스레드의 interrupted 필드값을 반환
    - false로 값을 변경
public class SleepThread2 implements Runnable{
   @Override
   public void run() {
      int count = 0;
      // Thread.currentThread().isInterrupted()
      // 현재 스레드의 interrupted 필드 값 반환
      // -> 수행 중이면 false, 멈춰야되면 true

      // interrputed 필드 값이 false면 계속 반복 / true면 종료
      while(!Thread.currentThread().isInterrupted()) {
          try { // 0.5초마다 count를 1씩 증가하며 출력
              Thread.sleep(500); // 0.5초
              System.out.println(++count);
          } catch (InterruptedException e) {
            // sleep() 중 interrupt() 메소드가 호출되면
            // InterruptedException이 발생하고
            // interrupted 필드 값이 true로 변하지 못하게 된다.
            // -> catch문에서 interrupted 필드 값을 true로 변경
            System.out.println("=== 인터럽트에 의해서 종료 ===");
            Thread.currentThread().interrupt();
          }
      }
   }
}
/*---------------------------------------------------------*/
public class ThreadControlRun {
   public static void main(String[] args) {
      Thread sleepThread2 = new Thread( new SleepThread2() );
          sleepThread2.start();
   }
}
/*=========================================================*/

public class InterruptTest {
   public void test() {
      Thread sleep2 = new Thread( new SleepThread2() );
		
      sleep2.start(); // SleepTread2의 run() 메소드 실행
		
      System.out.println("<엔터 입력 시 종료>");
      Scanner sc = new Scanner(System.in);
      sc.nextLine(); // 입력 버퍼에서 다음 \n까지 읽어오기
                     // 단, 버퍼가 비어있으면
                     // 다음 \n(엔터) 입력 시까지 현재 스레드 무한 대기
      // 무한 대기 중...
		
      // 엔터 입력 시 sleep2 스레드를 멈추게 하는 interrupt() 호출
      sleep2.interrupt();
   }
}
/*---------------------------------------------------------*/
public class ThreadControlRun {
   public static void main(String[] args) {
      InterruptTest it = new InterruptTest();
      it.test();
   }
}
  • sleep( ) + interrupt( ) 예제 - 2
public class ThreadControlRun {
   public static void main(String[] args) {
      StopWatchContoller stopWatch = new StopWatchContoller();
      stopWatch.watchStart();
   }
}
/*-----------------------------------------------------------------*/
public class StopWatchContoller {
   public void watchStart(){
      // StopWatch 스레드 생성
      Thread stopWatch = new Thread(new StopWatch());
      // 스톱워치 실행
      stopWatch.start();
      // 엔터 입력 시까지 무한 대기
      Scanner sc = new Scanner(System.in);
      sc.nextLine();
      // StopWatch 스레드 멈추게 하기
      stopWatch.interrupt();
   }
}
/*-----------------------------------------------------------------*/
public class StopWatch implements Runnable{
   @Override
   public void run() {
        int count = 0;
        int min = 0; // 분
        int sec = 0; // 초

        while(!Thread.currentThread().isInterrupted()) {
           try {
               Thread.sleep(10); // 1/100초
               count++;          // 0.01초마다 카운트 증가
				
               if(count == 100) { // 1초가 됐을 때
                  sec++;
                  count = 0;
               }
               if(sec == 60) { // 1분이 됐을 때
                  min++;
                  sec = 0;
               }
               for(int i=0; i<30; i++) System.out.println();
				
               System.out.printf("%02d분 %02d초 %02d \n",min,sec,count);
           } catch (InterruptedException e) {
                System.out.println("=== 종료 ===");
                Thread.currentThread().interrupt(); // false -> true
                // 현재 StopWatch 스레드에서 interrupt() 호출
           }
      }
   }
}

 

'Java > 기본 개념' 카테고리의 다른 글

변수(Variable)  (0) 2024.12.16
프로그래밍 기초  (0) 2024.12.16
컬렉션(Collection)  (2) 2024.11.17
네트워크(Network)  (1) 2024.11.17
입출력 (IO_Input / Output)  (3) 2024.11.17