본문 바로가기
Java/Java

[Java] 스레드(thread)

by 전재경 2022. 12. 8.

프로세스(process)

  • 프로세스(process)란 단순히 실행 중인 프로그램(program)
  • 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것
  • 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성

 

스레드(thread)

  • 스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체
  • 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행
  • 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다

 

멀티스레드

  • 멀티스레드 =multi thread =다중스레드
  • 멀티스레드는 여러개의 스레드를 이용하는 프로그램이다
  • 다중 스레드에서 각각의 스레드는 하나의 독립적인 프로세스처럼 작업 수행

 

작업관리자 > 성능 탭을 열어보면 프로세스와 스레드를 확인할 수 있다

 

멀티 스레드로 구현된 프로그램을 실행

  • 하나의 프로세서는 한번에 스레드1개밖에 실행시키지 못한다
  • 대신 일정한 시간 간격으로 수행해야하는 스레드를 전환한다
  • 스레드를 전환할 때는 운영체제의 스케줄러의 기준에 따라 순서가 정해지게 된다
  • 여러 스레드를 번갈아 처리하기 때문에 엄밀히 말하면 한번에 한가지를 처리하지만 동시에 작업하는 듯한 효과를 준다
  • 이와 같은 방식을 시분할 방식 이라고 한다

 

멀티스레드 적용하기 위한 조건

  • 병행성(concurrency) : 다수의 스레드 생성방법 존재
  • 동기화(synchronization) : 작업이 방해받지 않고 각 스레드의 동기화 방법 존재
  • 통신(communication) : 서로 다른 스레드가 정보를 교환할 수 있는 방법이 존재

 

스레드의 생성과 실행

생성하는 방법

  1. Runnable 인터페이스를 구현하는 방법
  2. Thread 클래스를 상속받는 방법

 

두 방법 모두 스레드를 통해 작업하고 싶은 내용을 run() 메소드에 작성하면 된다.

위 두가지 방법을 사용하여 스레드를 생성하고 실행하는 예제

class ThreadWithClass extends Thread {

    public void run() {

        for (int i = 0; i < 5; i++) {

            System.out.println(getName()); // 현재 실행 중인 스레드의 이름을 반환함.

            try {

                Thread.sleep(10);          // 0.01초간 스레드를 멈춤.

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

 

class ThreadWithRunnable implements Runnable {

    public void run() {

        for (int i = 0; i < 5; i++) {

            System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함.

            try {

                Thread.sleep(10);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

 

public class Thread01 {

    public static void main(String[] args){

        ThreadWithClass thread1 = new ThreadWithClass();       // Thread 클래스를 상속받는 방법

        Thread thread2 = new Thread(new ThreadWithRunnable()); // Runnable 인터페이스를 구현하는 방법

 

        thread1.start(); // 스레드의 실행

        thread2.start(); // 스레드의 실행

    }

}
//결과

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

 

생성된 스레드가 서로 번갈아가며 실행되고 있는 것을 확인

 

Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없으므로,

일반적으로 Runnable 인터페이스를 구현하는 방법으로 스레드를 생성

 

스레드의 우선순위

  • 자바에서 각 스레드는 우선순위(priority)에 관한 자신만의 필드를 가지고 있다
  • 우선순위에 따라 특정 스레드가 더 많은 시간 동안 작업을 할 수 있도록 설정할 수 있다

 

필드 특징
static int MAX_PRIORITY 스레드가 가질 수 있는 최대 우선순위를 명시함.
static int MIN_PRIORITY 스레드가 가질 수 있는 최소 우선순위를 명시함.
static int NORM_PRIORITY 스레드가 생성될 때 가지는 기본 우선순위를 명시함.

 

getPriority()와 setPriority() 메소드를 통해 스레드의 우선순위를 반환하거나 변경할 수 있다.

스레드의 우선순위가 가질 수 있는 범위는 1부터 10까지이며, 숫자가 높을수록 우선순위 또한 높아진다.

 

  • 스레드의 우선순위는 비례적인 절댓값이 아닌 어디까지나 상대적인 값
  • 우선순위가 10인 스레드가 우선순위가 1인 스레드보다 10배 더 빨리 수행되는 것은 아니다.
  • 단지 우선순위가 10인 스레드는 우선순위가 1인 스레드보다 좀 더 많이 실행 큐에 포함되어,
    좀 더 많은 작업 시간을 할당받을 뿐
class ThreadWithRunnable implements Runnable {

    public void run() {

        for (int i = 0; i < 5; i++) {

            System.out.println(Thread.currentThread().getName()); // 현재 실행 중인 스레드의 이름을 반환함.

            try {

                Thread.sleep(10);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}

 

public class Thread02 {

    public static void main(String[] args){

        Thread thread1 = new Thread(new ThreadWithRunnable());

        Thread thread2 = new Thread(new ThreadWithRunnable());


 

①      thread2.setPriority(10); // Thread-1의 우선순위를 10으로 변경함.

 

②      thread1.start(); // Thread-0 실행

③      thread2.start(); // Thread-1 실행

 

        System.out.println(thread1.getPriority());

        System.out.println(thread2.getPriority());

    }

}
//결과

5

10

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

Thread-1

Thread-0

main() 메소드를 실행하는 스레드의 우선순위는 언제나 5이므로,

main() 메소드 내에서 생성된 스레드 Thread-0의 우선순위는 5로 설정되는 것을 확인할 수 있다.

 

만약 ①번 라인이 존재하지 않는다면, Thread-0이 먼저 실행되고, Thread-1이 나중에 실행

하지만 ①번 라인에서 Thread-1의 우선순위를 10으로 변경했기 때문에,

Thread-1이 나중에 실행됐더라도 우선순위가 Thread-0보다 높아 먼저 실행되는 것

 

스레드의 상태

상태
구분 내용
초기 상태 NEW 스레드 객체를 생성한 상태
실행 가능  RUNNABLE start()메서드로 객체를 호출하여 실행할 수 있는 상태
실행 중 RUNNING 실행가능한 스레드 중에서 스케줄러가 선택하여 스레드가 실행 중인 상태.
CPU에 의해 코드가 한줄씩 실행된다. CPU는 n개의 스레드 중 1개밖에 처리할 수 없으며 
n-1개의 스레드는 대기상태가 된다
대기상태 WATING 실행 중지. 다른 스레드의 통보(notify)를 기다리는 상태
TIMED_WATING 실행 중지. 주어진 시간동안 대기상태
BLOCKED 다른 스레드에서 락(lock)을 획득한 이유로 락이 걸린 상태
종료 TERMINATED 스레드가 종료된 상태. 한번 종료되면 다시 시작될 수 없다.

스레드 상태는 getState() 스태틱 메서드를 이용해 확인할 수 있다.

 

스레드 동기화(synchronized)

 

멀티스레드 프로그램은 스레드끼리 객체를 공유해 작업하는 경우가 있다. synchronized 키워드를 사용하면 특정 스레드가 사용중인 객체에 다른 스레드가 접근할 수 없게 되어 동기화가 보장된다. synchronized 키워드는 인스턴스, 정적 메서드, 특정 코드 영역 등에 붙일 수 있다.

  • 임계영역(critical section)이란 스레드 1개만 실행하거나 접근이 가능한 자원 및 코드의 범위를 말한다.
  • 락(lock)이란 공유객체에 여러 스레드가 접근하지 못하게 하는 과정이다. 락은 모든 객체가 메모리 힙 영역에 생성될 때 자동으로 할당된다.

스레드 동기화 과정

특정 스레드 A가 실행중(Running) synchronized 선언된 블록을 만나게 되면 object's Lock Pool 로 이동하고 (lock)을 획득한다. 다른 스레드는 Running  Blocked 되어 실행가능한(Runnable) 상태가 된다.
특정 스레드 A synchronized 블록 실행이 종료된다면 A 락을 해제한다.

A가 락을 해제하면 다른 스레드 B가 실행가능한(Runnable)상태에서 실행(Running)을 하게 된다. 만약 스레드 B synchronized 선언된 블록을 만나면 B object's Lock Pool 로 이동하고 락(lock)을 획득한다. 이후 과정은 앞과 동일

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

[Java] 예외  (1) 2022.12.07
[Java] 타입 변환과 다형성  (0) 2022.12.06
[Java] 인터페이스(Interface)  (0) 2022.12.05
[Java] 추상 클래스, 메소드  (0) 2022.12.02
[Java]타입 변환과 다형성  (0) 2022.11.30

댓글