KoreaIt Academy/JAVA

23. JAVA의 쓰레드(Thread), 동기화(synchronized), 상태제어(wait, join, sleep, interrupt, interrupted)

hongeeii 2021. 7. 28.
728x90
반응형

Thread

- 운영체제로부터 메모리를 할당받아 실행 중인 프로그램을 프로세스(process)라고 합니다.

- 쓰레드(Thread)란 프로세스 내에서 실행하는 흐름의 단위 입니다.

 

멀티 프로세스 : 서로 독립적으로, 하나의 프로세스에서 오류가 발생해도 다른 프로세스에게 영향을 미치지 않습니다.

 

단일 쓰레드 특징
- 처리 경로를 한 개만 가지고 있기 때문에 직렬적입니다.
- 동시에 많은 양을 처리하기 힘들기 때문에 상대적으로 비효율적입니다.
- 하지만 하나의 작업에 문제가 발생하더라도 다른 작업에는 영향을 끼치지 않습니다.
- 따라서 안정성이 보장됩니다.
- 설계도 멀티 쓰레드에 비해 쉽습니다.

멀티 쓰레드
- 하나의 프로세스를 동시에 처리하는 것처럼 보이지만 사실은 매우 짧은 단위로 분할해서 차례로 처리합니다.

- 여러 개의 처리 경로를 가질 수 있도록 하며, 동시 작업이 가능해집니다.
- 설계하기 굉장히 어려우며, 하나의 쓰레드 문제 발생 시 모든 쓰레드에 문제가 발생하게 됩니다.
- JAVA 웹 개발 시 사용되는 웹 서버가 대표적인 멀티 쓰레드입니다.
- 멀티 쓰레드로 설계했다면, 처리량 증가, 효율성 증가, 처리비용 감소의 장점이 있기 때문에
  단점을 감수하고 설계하는 편입니다.

멀티쓰레드의 골칫덩어리 - 교착상태(DeadLock)
멀티 쓰레드 중 쓰레드 간에 대기 상태가 종료되지 않아서 무한정 대기만 하는 비정상적인 상태.
교착상태인지를 판단했다면 전체 쓰레드를 깨워주거나, 하나의 쓰레드를 종료시켜주면 교착상태가 해결됩니다.

 

 

 

Thread 구현 방법

※ 핵심 : run()메소드 재정의

 

1. Runnable 인터페이스 지정

2. Thread 클래스 상속

 

- Thread 클래스로부터 직접 생성 

Thread thread = new Thread(Runnable target);

<Thread 생성자>
Thread(String s)
스레드 이름
 Thread(Runnable r) 인터페이스 객체 
 Thread(Runnable r, String s) 인터페이스 객체와 스레드 이름 

Runnable은 작업 스레드가 실행할 수 있는 코드를 가지고 있는 객체입니다.

Runnable은 인터페이스 타입이라 run()메소드를 재정의 해야합니다.

코드를 절약하기 위해 익명 구현 객체로도 사용이 가능하고, 함수적 인터페이스라 람다식도 사용이 가능합니다.

/////첫 번째 방법/////
Rannable task = new Thread2();
Thread thread = new Thread(task);

/////두 번째 방법/////
Thread thread = new Thread(new Runnable(){
	public void run(){
    	//스레드가 실행할 코드;
    }
});

/////세 번째 방법/////
Thread thread = new Thread(()->{
	///스래드가 실행할 코드;
});

 

 

- Thread 하위 클래스로부터 생성

class ThreadA extends Thread{

    public void run(){
      // 수행할 문장들 기술
    }
}

- 실제 사용

- ThreadA ta = new ThreadA();

  ta.start();

 

 

동기화 메소드와 동기화 블록

멀티 쓰레드에서는 쓰레드들이 객체를 공유해서 작업을 할때,

쓰레드가 사용중인 객체를 다른 쓰레드가 변경할 수 없도록 하려면 쓰레드 작업이 끝날 때까지 객체에 잠금을 걸어서

다른 쓰레드가 사용할 수 없도록 해야 합니다.

 

동기화(Synchronized)
하나의 쓰레드가 실행 중일 때 다른 쓰레드가 동시에 같은 필드를 사용하지 못하게 막는 것입니다.
각 쓰레드를 제어할 수 있게 됩니다.

멀티 쓰레드 구현 시 자원의 특정 부분만 하나씩 처리하고 싶을 때 아래 두 가지 방법을 사용합니다.


1.synchronized(mutex){ ... }
일부 소스코드만 동기화를 걸어줍니다.

public void method() {
	//여러 쓰레드가 실행 가능한 영역
	     synchronized (///공유객체///) {
	        ///단 하나의 쓰레드만 실행
		}
	///여러 쓰레드가 실행 가능한 영역			
}


2.synchronized 키워드
키워드를 사용한 메소드 전체에 동기화를 걸어줍니다.

public synchronized void method() {
	///하나의 쓰레드만 실행
}

 

Thread 상태제어

 

Sleep() : 주어진 시간동안 일시 정지

실행 중인 쓰레드를 일정 시간 멈추게 하고 싶다면 Thread 클래스의 정적 메소드인 sleep()을 사용하면 됩니다.

매개값으로는 밀리세컨드(1/1000) 단위로 시간을 주면 됩니다.

Thread.sleep(1000); ///// 1초간 정지

 

join() : 다른 쓰레드의 종료를 기다림

쓰레드는 다른 쓰레드와 독립적으로 실행되지만, 공유객체를 사용할 경우 다른 쓰레드가 종료될 때까지 기다렸다가 

실행하는 경우에 사용합니다.

 

wait(), notify() : 쓰레드간 협업

 

두 개의 쓰레드를 교대로 번갈아가며 실행해야 할 경우가 있습니다.

wait()은 말 그대로 실행중인 쓰레드를 일시정지 상태로 만듭니다.

notify()wait()에 의해 일시 정지된 스레드 중 한 개를 실행 대기 상태로 만듭니다.

 

 

 

iterrupt() : 쓰레드의 안전한 종료

 

1. 필드에 boolean타입의 변수를 선언하고 run안에 있는 반복문에 해당 변수가 true일 때 break하도록 설계

 

2. sleep()을 사용한다면 일시정지 상태에서 InterruptedException을 발생시켜줌으로써 예외처리로 종료

   ※ 쓰레드객체.interrupt()를 사용하면 일시정지 상태에서 예외 발생

 

3. wait(), join()을 사용하는 로직이라면, sleep()사용한 종료를 진행해야하고,

   만약 위의 메소드를 사용하지 않는 로직이라면 sleep()을 사용하지 않아도 종료가 가능합니다.

   ※ 쓰레드객체.interrupt()를 사용하면 Thread.interrupted() 의 상태가 true로 변경됩니다. 

 

쓰레드는 자신의 run()메소드가 모두 실행되면 자동적으로 종료됩니다.

그러나 실행 중인 쓰레드를 즉시 종료하려고 하는 경우가 있습니다.

interrupt()메소드는 쓰레드가 일시 정지 상태에 있을때 InterruptedException 예외를 발생 시켜 

run()메소드를 정상 종료시킬 수 있습니다.

728x90
반응형

추천 글