13 멀티태스킹
프로세스와 스레드
프로세스 (Process)
정의: 프로세스는 실행 중인 프로그램의 인스턴스로, 프로그램이 메모리에서 실행되고 있는 독립된 작업 단위입니다. 운영체제는 각 프로세스에 고유한 메모리 공간과 시스템 자원을 할당합니다. 프로세스는 여러개의 스레드를 포함할 수 있는 단위라고 할 수 있습니다.
특징:
독립된 메모리: 각 프로세스는 운영체제로부터 독립된 메모리 공간(코드, 데이터, 힙, 스택)을 할당받습니다. 따라서 한 프로세스의 메모리에 다른 프로세스가 직접 접근할 수 없습니다.
자원 할당: 프로세스는 실행에 필요한 CPU 시간, 메모리, 파일 핸들, 네트워크 연결 등의 자원을 할당받습니다.
멀티프로세싱: 하나의 운영체제에서 여러 프로세스가 동시에 실행되는 것을 멀티프로세싱이라고 합니다.
무거운 작업 단위: 프로세스 간의 통신은 IPC(Inter-Process Communication) 메커니즘을 사용하며, 컨텍스트 스위칭 비용이 큽니다.
예시:
우리가 컴퓨터에서 실행하는 각 프로그램(예: 웹 브라우저, 텍스트 편집기)은 각각 독립된 프로세스입니다.
프로세스 간에는 메모리 공간이 분리되어 있어서 서로 간섭 없이 실행됩니다.
스레드 (Thread)
정의: 스레드는 프로세스 내에서 실행되는 작업의 최소 단위입니다. 하나의 프로세스는 여러 스레드를 포함할 수 있으며, 프로세스의 메모리 공간을 공유하면서 작업을 수행합니다.
특징:
공유 메모리: 같은 프로세스 내에서 여러 스레드는 프로세스의 코드, 데이터, 힙을 공유합니다. 그러나 각 스레드는 고유한 스택과 레지스터를 가지고 있습니다.
가벼운 작업 단위: 스레드 간의 통신은 메모리 공간을 공유하기 때문에 프로세스 간 통신보다 훨씬 빠르고 효율적입니다.
멀티스레딩: 하나의 프로세스에서 여러 스레드가 동시에 실행되는 것을 멀티스레딩이라고 합니다. 스레드를 사용하면 프로세스가 동시에 여러 작업을 수행할 수 있습니다.
컨텍스트 스위칭: 스레드 간의 컨텍스트 스위칭은 프로세스 간의 컨텍스트 스위칭보다 비용이 적습니다.
예시:
웹 브라우저는 여러 탭을 열어 각각의 웹 페이지를 로드하고, 이때 각 탭은 스레드로 실행될 수 있습니다.
게임에서는 그래픽 렌더링, 사용자 입력 처리, 네트워크 통신 등이 각각 별도의 스레드로 실행될 수 있습니다.
프로세스와 스레드의 비교
특성
프로세스(Process)
스레드(Thread)
메모리
독립된 메모리 공간(코드, 데이터, 힙, 스택)
프로세스 내에서 메모리(코드, 데이터, 힙)를 공유하고 스택은 별도
통신
프로세스 간 통신은 IPC(파이프, 소켓 등) 사용
공유 메모리 공간을 사용하여 통신이 용이
컨텍스트 스위칭
비용이 높음
비용이 상대적으로 낮음
자원 할당
운영체제가 개별적으로 자원을 할당
스레드는 프로세스의 자원을 공유
독립성
서로 독립적이며 한 프로세스의 오류가 다른 프로세스에 영향을 주지 않음
같은 프로세스 내에서는 서로 영향을 줄 수 있음
4. 프로세스와 스레드의 관계
프로세스는 하나 이상의 스레드를 포함할 수 있으며, 최소한 하나의 스레드(main thread)를 가지고 있습니다.
여러 스레드를 생성하여 프로세스 내의 자원을 공유하며 작업을 수행할 수 있습니다. 이를 통해 멀티스레딩을 활용하여 프로그램의 성능을 향상시킬 수 있습니다.
자바에서의 스레드
자바에서는 멀티스레딩을 쉽게 구현할 수 있도록 Thread
클래스와 Runnable
인터페이스를 제공합니다. 자바의 스레드는 JVM이 관리하는 프로세스 내에서 실행되며, 다음과 같이 사용할 수 있습니다.
스레드 생성 예시
edu.ch13.thread.basicThread 패키지 - ThreadExample 클래스
run()
메소드: 각 스레드의 작업을 정의하는 메소드입니다.for
루프를 사용하여 각 작업이 다섯 번 반복되도록 구현했습니다.start()
메소드: 스레드를 시작하기 위해start()
메소드를 호출합니다. 이 메소드는 내부적으로run()
메소드를 실행하며, 스레드를 병렬로 실행합니다.
Runnable 인터페이스를 사용한 스레드 생성
edu.ch13.thread 패키지 - RunnableExample 클래스
Runnable과 Thread의 차이
항목
Runnable
Thread
상속 여부
다른 클래스를 상속받을 수 있음
이미 Thread
를 상속받았으므로 다른 상속 불가
구현 방식
Runnable
인터페이스 구현
Thread
클래스를 직접 상속
유연성
더 유연한 설계 가능
덜 유연 (다중 상속 불가)
실행 방식
Thread
객체에 전달하여 실행
start()
메서드로 직접 실행
스레드 생성 및 sleep
사용
sleep
사용sleep
메소드를 사용하여 스레드가 1초(1000밀리초) 동안 잠드는 예시를 보여줍니다:
edu.ch13.thread 패키지
출력 결과
위 코드는 main
스레드가 "작업 시작"
메시지를 출력한 후 Thread.sleep(2000)
에 의해 2초 동안 일시 중지된 다음 "작업 종료"
메시지를 출력합니다.
다중 스레드에서의 사용 예시
sleep
메소드는 멀티스레드 환경에서 특정 스레드의 실행을 일시 중지할 때 유용합니다. 다음은 두 개의 스레드가 sleep
을 사용하여 번갈아가며 실행되는 예시입니다:
edu.ch13.thread 패키지 - MultiThreadSleepExample 클래스
출력 결과 (예시)
실행할 때마다 결과가 다를 수 있습니다. 일반적으로 두 스레드가 1초 간격으로 번갈아 실행됩니다.
주의사항
현재 실행 중인 스레드만 일시 중지됩니다: Thread.sleep()
은 호출한 스레드에만 적용되며, 다른 스레드를 일시 중지시키지 않습니다. 예시로 메인 인스턴스에서 호출하게 되면 다른 스레드에서 적용되는 것이 아닌 메인 인스턴스만 일시 중지 됩니다.
스레드 다루기
스레드 이름 설정하기
1. setName()
메소드
setName()
메소드스레드 객체를 생성한 후에 setName()
메소드를 사용하여 스레드의 이름을 설정할 수 있습니다.
edu.ch13.thread 패키지
2. 스레드 이름 가져오기
현재 실행 중인 스레드의 이름을 가져오려면 Thread.currentThread().getName()
메소드를 사용합니다. 위의 예제에서도 이 메소드를 사용하여 스레드의 이름을 출력하고 있습니다.
3. 스레드 이름의 기본값
스레드를 생성할 때 이름을 지정하지 않으면, JVM은 자동으로 "Thread-n"
형식의 이름을 할당합니다. 여기서 n
은 스레드가 생성된 순서대로 증가하는 숫자입니다.
4. .join()
메소드
.join()
메소드.join()
메소드는 자바의 Thread
클래스에서 제공되는 메소드로, 현재 실행 중인 스레드가 다른 스레드의 종료를 기다리도록 하는 데 사용됩니다. 즉, 한 스레드가 다른 스레드의 작업이 완료될 때까지 일시 중지되어 기다리는 것을 의미합니다.
예를 들어, thread1.join()
을 호출하면 현재 스레드는 thread1
이 작업을 완료할 때까지 기다리게 됩니다. thread1
이 완료되면 현재 스레드는 그 이후의 작업을 계속 진행합니다.
스레드를 사용한 멀티태스킹
멀티태스킹은 한 번에 여러 작업을 수행하는 것을 말하며, 자바에서 스레드를 사용하여 멀티태스킹을 구현할 수 있습니다. 멀티태스킹은 CPU를 효율적으로 활용하여 응용 프로그램의 성능과 응답성을 향상시킵니다. 자바에서 멀티태스킹은 여러 스레드를 생성하고 실행하여 동시에 여러 작업을 수행하는 방식으로 이루어집니다.
다음은 스레드를 사용하여 멀티태스킹을 구현하는 예시와 코드 설명을 제공하겠습니다.
1. 멀티태스킹 구현 예시
아래 예제에서는 두 개의 작업을 수행하는 두 개의 스레드를 생성하고, 각 스레드가 독립적으로 실행되도록 합니다.
예제 : 멀티태스킹 구현
edu.ch13.thread 패키지 - MultiTaskingExample1 클래스
sleep()
메소드: 각 스레드의 실행을 잠시 멈추기 위해 Thread.sleep()
을 사용하였습니다. sleep
메소드를 통해 스레드 간의 실행 순서를 섞어 멀티태스킹의 효과를 확인할 수 있습니다.
멀티태스킹의 장점과 주의사항
장점:
CPU 활용도 향상: 스레드가 동시에 여러 작업을 수행하여 멀티코어 프로세서의 성능을 최대한 활용할 수 있습니다.
응답성 개선: 특히 GUI 애플리케이션이나 서버 애플리케이션에서 멀티스레딩은 응답 시간을 개선하고 사용자 경험을 향상시킵니다.
주의사항:
동기화 문제: 여러 스레드가 동일한 자원에 동시에 접근할 때 데이터 불일치나 경쟁 조건이 발생할 수 있습니다. 이런 경우 동기화(synchronization) 메커니즘을 사용하여 문제를 해결해야 합니다.
자원 소모: 과도한 스레드 생성은 시스템의 메모리 및 CPU 자원을 소모할 수 있으므로, 스레드 풀과 같은 자원 관리 기법을 사용하는 것이 좋습니다.
스레드 그룹과 데몬 스레드
스레드 그룹(Thread Group)
스레드 그룹(Thread Group)은 여러 스레드를 하나의 그룹으로 묶어 관리하는 기능을 제공합니다. 스레드 그룹을 사용하면 스레드의 우선순위 설정, 일괄 시작/중지, 데몬 스레드 설정 등을 그룹 단위로 쉽게 관리할 수 있습니다.
스레드 그룹은 주로 스레드를 논리적으로 분류하거나 관련된 스레드를 함께 제어할 필요가 있을 때 사용됩니다.
1. 스레드 그룹 생성 및 사용 예시
edu.ch13.thread 패키지
출력 예시
코드 설명
스레드 그룹 생성:
ThreadGroup group = new ThreadGroup("MyThreadGroup");
를 통해 새로운 스레드 그룹을 생성합니다.스레드 그룹에 스레드 추가:
new Thread(group, ...)
형식으로 스레드를 생성하면서 스레드 그룹을 지정할 수 있습니다.스레드 그룹 정보 출력:
group.getName()
으로 그룹의 이름을 얻을 수 있으며,group.list()
로 그룹에 포함된 스레드의 정보를 출력할 수 있습니다.
스레드 그룹의 주요 메소드
getName()
: 스레드 그룹의 이름을 반환합니다.list()
: 그룹 내 모든 스레드와 하위 스레드 그룹의 정보를 출력합니다.activeCount()
: 그룹 내 활성화된 스레드의 수를 반환합니다.interrupt()
: 그룹 내 모든 스레드를 일괄적으로 중단(interrupt)할 수 있습니다.
데몬 스레드(Daemon Thread)
데몬 스레드(Daemon Thread)는 백그라운드에서 실행되는 스레드로, 주 스레드가 종료되면 함께 종료되는 특성을 가지고 있습니다. 일반적으로 데몬 스레드는 JVM의 작업을 보조하는 역할을 하며, 예를 들어 가비지 컬렉터, 로그 기록, 파일 청소 등의 작업을 수행합니다.
데몬 스레드의 특징
백그라운드 작업: 데몬 스레드는 메인 스레드와 같은 주요 작업을 수행하지 않으며, 주로 백그라운드에서 보조적인 작업을 수행합니다.
주 스레드 종료 시 자동 종료: 모든 일반 스레드(Non-Daemon Thread)가 종료되면 데몬 스레드는 자동으로 종료됩니다.
설정 방법: 스레드의
setDaemon(true)
메소드를 호출하여 데몬 스레드로 설정할 수 있습니다. 이 메소드는 스레드가 시작되기 전에만 호출할 수 있습니다.
데몬 스레드 설정 예시
edu.ch13.thread 패키지
출력 예시:
3초 후에 "메인 스레드 인터럽트 시도..." 메시지가 출력되며, 메인 스레드가 인터럽트됩니다.
이후 메인 스레드는
InterruptedException
을 발생시키고"메인 스레드 인터럽트됨"
메시지를 출력한 후 종료됩니다.
코드 설명
데몬 스레드 설정:
daemonThread.setDaemon(true);
로 스레드를 데몬 스레드로 설정했습니다. 이는 스레드가 시작되기 전에만 설정할 수 있습니다.데몬 스레드의 실행: 데몬 스레드는 백그라운드에서 무한 루프로 실행되도록 설정되어 있지만, 메인 스레드가 종료되면 자동으로 종료됩니다.
데몬 스레드는 1초 간격으로 "데몬 스레드 실행 중..."을 출력합니다. 이 스레드는 메인 스레드가 종료되면 자동으로 종료됩니다.
interruptingThread
:새로운 스레드
interruptingThread
는 3초 동안 대기(Thread.sleep(3000)
)한 후, 메인 스레드를 인터럽트합니다.Thread.currentThread().getThreadGroup().getParent().interrupt()
를 호출하여 현재 실행 중인 메인 스레드를 인터럽트합니다.
메인 스레드:
메인 스레드는 5초 동안
Thread.sleep(5000)
으로 일시 중지됩니다.그러나
interruptingThread
에 의해 3초 후에 메인 스레드는 인터럽트되어InterruptedException
이 발생하고,"메인 스레드 인터럽트됨"
메시지가 출력됩니다.
동기화**(Synchronization)**
동기화(Synchronization)란?
동기화(Synchronization)는 멀티스레드 환경에서 여러 스레드가 동시에 공유 자원(예: 변수, 객체)에 접근할 때 발생할 수 있는 문제를 방지하기 위해 사용되는 기법입니다.
자바에서 동기화는 주로 임계 영역(Critical Section)을 보호하여 스레드 간의 데이터 불일치를 예방하고, 데이터의 일관성을 유지하는 데 사용됩니다.
동기화가 없다면 여러 스레드가 동시에 공유 자원에 접근할 수 있으며, 이로 인해 데이터 경쟁(Race Condition)이나 데이터 손상(Corruption)이 발생할 수 있습니다. 동기화를 통해 공유 자원에 대한 스레드의 접근을 제어하면 이러한 문제를 방지할 수 있습니다.
1. 자바에서의 동기화 구현 방법
자바에서는 동기화를 구현하기 위해 synchronized
키워드와 동기화 블록(Synchronized Block)을 사용합니다.
1.1 synchronized
메소드
synchronized
메소드method
앞에 synchronized
키워드를 사용하여 해당 메소드가 한 번에 하나의 스레드에 의해서만 실행될 수 있도록 합니다.
edu.ch13.synchronization.synchronizedMethod 패키지
synchronized
키워드:increment
메소드가 동기화되어 있으므로, 여러 스레드가 동시에 이 메소드를 호출해도 한 번에 하나의 스레드만 실행할 수 있습니다.임계 영역:
count++
연산은 임계 영역으로 보호되며, 다른 스레드가increment
메소드를 실행하려면 현재 실행 중인 스레드가 메소드를 완료하고 나서야 가능합니다.
1.2 synchronized
블록
synchronized
블록method
전체가 아니라 특정 코드 블록만 동기화하고자 할 때, synchronized
블록을 사용합니다. 이 방식은 주로 특정 객체에 대한 동기화가 필요한 경우에 사용됩니다. edu.ch13.synchronization.synchronizedBlock 패키지
synchronized (this)
: 현재 인스턴스(this
)에 대한 동기화를 수행합니다. 이 블록 내의 코드는 한 번에 하나의 스레드만 실행할 수 있습니다.동기화 대상:
synchronized
블록에 사용된 객체(여기서는this
)를 모니터(lock)라고 부르며, 이 객체를 통해 스레드 간의 접근을 제어합니다.
1.3 synchronized
블록을 사용하여 특정 객체 동기화
synchronized
블록을 사용하여 특정 객체 동기화edu.ch13.synchronization.synchronizedObject 패키지
다른 객체에 대한 동기화도 가능합니다. 예를 들어, 공유 객체 lock
을 사용하여 동기화할 수 있습니다.
2. 동기화의 중요성: 동기화 문제 예시
동기화가 이루어지지 않은 경우 여러 스레드가 동시에 자원에 접근하여 데이터 경쟁이 발생할 수 있습니다.
동기화 없이 동작하는 코드 예시
edu.ch13.synchronization.importance 패키지
출력 예시
예상 출력은
2000
이지만, 스레드 간 경쟁으로 인해 실제로는 예측할 수 없는 값이 출력될 수 있습니다.count++
연산은 세 단계로 나누어지는데, 이 과정 중 다른 스레드가 개입하여 올바른 값이 저장되지 않을 수 있습니다.
3. 동기화된 코드로 문제 해결
위 코드를 synchronized
키워드를 사용하여 동기화하면 예상한 결과를 얻을 수 있습니다. edu.ch13.synchronization.importance 패키지
출력 예시
4. 동기화의 영향
성능 저하: 동기화를 사용하면 여러 스레드가 임계 영역에 접근하는 속도가 제한되므로 프로그램의 성능이 저하될 수 있습니다. 따라서 필요한 부분에만 동기화를 적용해야 합니다.
교착 상태(Deadlock): 동기화된 코드가 잘못 설계되면 교착 상태가 발생할 수 있습니다. 여러 스레드가 서로의 락을 기다리면서 영원히 대기하는 상태가 될 수 있으므로, 동기화 블록의 사용을 신중하게 설계해야 합니다.
5. 정리
동기화는 여러 스레드가 동시에 공유 자원에 접근하는 것을 제어하여 데이터의 일관성을 유지하는 데 사용됩니다.
자바에서는
synchronized
키워드를 사용하여 메소드 전체 또는 특정 블록을 동기화할 수 있습니다.동기화는 데이터 경쟁과 데이터 손상을 방지하지만, 성능에 영향을 줄 수 있으므로 필요한 곳에만 사용해야 합니다.
동기화 블록을 잘못 설계하면 교착 상태가 발생할 수 있으므로, 주의하여 작성해야 합니다.
스레드 풀
스레드 풀(Thread Pool)이란?
스레드 풀(Thread Pool)은 미리 일정한 수의 스레드를 생성해두고, 작업 큐에서 요청된 작업들을 처리하는 스레드 관리 기법입니다. 스레드 풀을 사용하면 스레드를 효율적으로 관리하고, 스레드 생성 및 종료로 인한 비용을 줄일 수 있습니다. 이는 특히 동시성을 필요로 하는 대규모 작업에 매우 유용합니다.
쓰레드
업체 소유 물품
(대수 지정, 사용 후 반납 & 재사용)
Runnable
이용자
(모두가 물품 이용중이면 대기)
1. 스레드 풀의 필요성
성능 향상: 매번 작업이 필요할 때마다 새로운 스레드를 생성하고 종료하는 것은 상당한 비용이 듭니다. 스레드 풀은 미리 생성된 스레드를 재사용하여 이 비용을 줄입니다.
자원 관리: 시스템에서 생성할 수 있는 스레드의 수는 제한적입니다. 스레드 풀이 없으면 무한정 많은 스레드를 생성할 수 있어 시스템의 메모리나 CPU 자원을 낭비하고, 심지어 시스템 다운을 초래할 수 있습니다. 스레드 풀은 제한된 수의 스레드를 생성하여 자원을 효율적으로 관리합니다.
과부하 방지: 스레드 풀이 있으면 지정된 수의 스레드만 활성화되기 때문에, 시스템에 무리한 부하를 주지 않고 안정적으로 동작할 수 있습니다.
2. 자바에서 스레드 풀 사용: Executor
프레임워크
Executor
프레임워크자바에서는 Executor
프레임워크를 통해 스레드 풀을 간편하게 사용할 수 있습니다. Executor
인터페이스는 스레드를 관리하고 작업을 실행하는 방법을 정의하며, 다양한 종류의 스레드 풀을 제공하는 정적 메소드를 포함하는 Executors
클래스와 함께 사용됩니다.
2.1. Executors
클래스로 스레드 풀 생성하기
Executors
클래스로 스레드 풀 생성하기newFixedThreadPool(int nThreads)
: 고정된 크기의 스레드 풀을 생성합니다. 스레드 풀 내의 스레드 수는 항상 고정되어 있으며, 모든 스레드가 사용 중일 때 새로운 작업이 도착하면 대기 큐에 추가됩니다.newCachedThreadPool()
: 필요한 경우 스레드를 생성하고, 사용하지 않는 스레드를 재사용합니다. 시스템에 부하를 줄 수 있어 주의해서 사용해야 합니다.newSingleThreadExecutor()
: 단일 스레드로 작업을 처리하는 스레드 풀을 생성합니다.newScheduledThreadPool(int nThreads)
: 주기적 또는 지연 작업을 처리하는 스레드 풀을 생성합니다.
3. 스레드 풀 사용 예제
다음 예제에서는 Executors.newFixedThreadPool()
메소드를 사용하여 고정된 크기의 스레드 풀을 생성하고, 여러 작업을 스레드 풀에 제출하여 처리하는 방법을 보여줍니다.
edu.ch13.threadPool 패키지
출력 예시
코드 설명
스레드 풀 생성:
Executors.newFixedThreadPool(5)
를 사용하여 5개의 스레드로 구성된 고정 크기 스레드 풀을 생성합니다.이 스레드 풀은 동시에 최대 7개의 작업을 처리할 수 있습니다.
작업 제출:
executor.submit()
메소드를 사용하여 8개의 작업을 스레드 풀에 제출합니다.스레드 풀의 크기가 5이므로 동시에 5개의 작업이 실행되며, 나머지 작업은 이전 작업이 완료될 때까지 대기합니다.
스레드 풀 종료:
executor.shutdown()
을 호출하여 스레드 풀을 종료합니다. 이 메소드는 더 이상 새로운 작업을 받지 않고, 현재 진행 중인 작업이 완료되면 스레드 풀을 종료합니다.
4. 스레드 풀의 주요 메소드
submit(Runnable task)
: 스레드 풀에 작업을 제출합니다.Runnable
또는Callable
객체를 인수로 받아 스레드 풀에서 실행합니다.shutdown()
: 스레드 풀이 더 이상 새로운 작업을 받지 않고, 현재 진행 중인 작업이 모두 완료되면 스레드 풀을 종료합니다.shutdownNow()
: 스레드 풀을 즉시 종료하고, 진행 중인 작업도 중단시킵니다.awaitTermination(long timeout, TimeUnit unit)
: 스레드 풀이 종료될 때까지 대기하며, 대기 시간 동안 종료되지 않으면false
를 반환합니다.
5. 주의사항
shutdown()
과shutdownNow()
:shutdown()
: 더 이상 새로운 작업을 받지 않지만, 이미 제출된 작업이 모두 완료될 때까지 스레드 풀을 유지합니다.shutdownNow()
: 즉시 스레드 풀을 종료하고, 진행 중인 작업도 중단시킵니다.
무한정한 스레드 생성 방지:
newCachedThreadPool()
은 필요한 경우 새로운 스레드를 계속 생성할 수 있으므로, 작업 수가 매우 많은 경우 조심해서 사용해야 합니다.자원 해제: 스레드 풀은 작업이 완료된 후에도 계속 유지되기 때문에 반드시
shutdown()
을 호출하여 스레드 풀을 종료하고 자원을 해제해야 합니다.
6. 정리
스레드 풀은 미리 생성된 스레드 집합을 사용하여 여러 작업을 동시에 처리하는 방식으로, 스레드 생성 및 소멸에 따른 비용을 최소화하고 시스템 자원을 효율적으로 관리합니다.
자바에서는
Executors
클래스를 통해 다양한 스레드 풀(newFixedThreadPool
,newCachedThreadPool
,newSingleThreadExecutor
,newScheduledThreadPool
)을 쉽게 생성할 수 있습니다.스레드 풀을 사용하여 멀티스레딩 작업을 수행할 때는 반드시
shutdown()
을 호출하여 스레드 풀을 적절히 종료해야 합니다.
Callable의 개념
자바에서 멀티스레딩을 사용하면 동시에 여러 작업을 수행할 수 있습니다. 이때, 작업을 실행하는 가장 기본적인 방법은 Runnable
을 사용하여 스레드를 만드는 것입니다. 그러나 Runnable
은 반환값이 없고 예외 처리를 할 수 없는 단순한 작업만 수행할 수 있습니다.
어떤 작업을 멀티스레딩으로 수행하면서 그 결과를 받아와야 하는 경우가 생깁니다. 예를 들어, 데이터베이스 쿼리, 파일 읽기, 계산 작업 등이 있으며 이러한 작업이 끝난 후 그 결과를 다시 사용할 수 있어야 합니다. 이러한 경우에 Callable
을 사용할 수 있습니다.
1. Callable
과 Runnable
의 차이점
Callable
과 Runnable
의 차이점Runnable
과 Callable
의 가장 큰 차이점은:
반환값:
Runnable
은 반환값이 없지만,Callable
은 제네릭을 사용하여 반환값을 지정할 수 있습니다.예외 처리:
Callable
은call()
메소드에서 체크 예외를 던질 수 있어 작업 중에 발생하는 예외를 호출하는 쪽에서 처리할 수 있습니다.
2. Callable
사용 예시
Callable
사용 예시edu.ch13.callable 패키지
코드 설명
Callable
인터페이스:Callable<Integer>
는Integer
타입의 결과를 반환하는 작업을 정의합니다.task
는 람다 표현식으로 정의된Callable
객체로,call()
메소드에서 1부터 10까지의 합을 계산하여 반환합니다.
call()
메소드 직접 호출:이 예시에서는
Callable
을 사용하지만Future
나 스레드 풀을 사용하지 않고, 단순히task.call()
메소드를 직접 호출하여 작업을 실행하고 그 결과를 출력합니다.예외가 발생할 수 있으므로
try-catch
블록으로 감싸서 예외를 처리합니다.
3. Callable
의 이점
Callable
의 이점반환값:
Callable
을 통해 작업의 결과를 반환할 수 있습니다.예외 처리:
Callable
은call()
메소드에서 체크 예외를 던질 수 있으므로, 작업 중 발생하는 예외를 유연하게 처리할 수 있습니다.복잡한 작업: 계산, 파일 I/O, 데이터베이스 액세스 등 복잡한 작업을 수행하는 데 적합합니다.
Future 개념
Future
는 자바의java.util.concurrent
패키지에서 제공되는 인터페이스로, 비동기 작업의 결과를 나타내는 객체입니다.Future
는 작업이 완료될 때까지 기다리거나, 작업을 취소하거나, 작업의 상태를 확인하고 결과를 가져오는 방법을 제공합니다. 이 인터페이스를 사용하면 현재 진행 중인 작업의 완료 여부를 비동기적으로 추적하고 그 결과를 나중에 사용할 수 있습니다.
1. Future
의 주요 메소드
Future
의 주요 메소드Future
는 비동기 작업의 상태와 결과를 관리하기 위한 여러 메소드를 제공합니다.
get()
: 작업이 완료될 때까지 현재 스레드를 블록(대기)하며, 완료되면 결과를 반환합니다.get(long timeout, TimeUnit unit)
: 작업이 완료될 때까지 주어진 시간만큼 기다립니다. 시간이 초과되면TimeoutException
을 발생시킵니다.cancel(boolean mayInterruptIfRunning)
: 작업을 취소합니다. 작업이 이미 완료되었거나, 취소할 수 없는 상태라면false
를 반환하고, 그렇지 않으면true
를 반환합니다.isDone()
: 작업이 완료되었는지 여부를 반환합니다. 완료 상태는 작업이 정상적으로 종료되거나, 취소되거나, 예외가 발생했을 때를 포함합니다.isCancelled()
: 작업이 취소되었는지 여부를 반환합니다.
2. Future
의 사용 예시
Future
의 사용 예시다음은 Callable
과 ExecutorService
를 사용하여 비동기 작업을 실행하고, Future
를 통해 그 결과를 가져오는 예시입니다.
edu.ch13.future 패키지
출력 예시
코드 설명
스레드 풀 생성:
Executors.newFixedThreadPool(2)
를 사용하여 두 개의 스레드로 구성된 스레드 풀을 생성합니다.
Callable
작업 정의:람다 표현식으로 정의된
Callable<String>
작업을 만듭니다.call()
메소드에서 2초 동안 작업을 수행한 후"작업 완료!"
라는 문자열을 반환합니다.
작업 제출 및
Future
객체 반환:executor.submit(task)
를 사용하여 작업을 비동기적으로 실행하고, 작업의 결과를Future<String>
객체로 받습니다.
isDone()
사용:futureResult.isDone()
을 호출하여 작업이 완료되었는지 확인합니다.작업이 아직 진행 중이므로 처음에는
false
가 출력됩니다.
get()
메소드로 결과 가져오기:futureResult.get()
메소드를 호출하면 현재 스레드가 작업이 완료될 때까지 대기하며, 작업이 완료되면 결과를 반환합니다.이 메소드는 예외(
InterruptedException
,ExecutionException
)를 던질 수 있으므로try-catch
블록으로 감싸서 예외를 처리합니다.
스레드 풀 종료:
모든 작업이 완료되었으므로
executor.shutdown()
을 호출하여 스레드 풀을 종료합니다.
CompletableFuture란?
CompletableFuture
는 자바 8에서 도입된 비동기 프로그래밍을 지원하는 클래스입니다.CompletableFuture
는 비동기 작업의 결과를 처리하고, 여러 비동기 작업을 결합하거나 후속 작업을 정의하는 데 사용할 수 있는 강력한 기능을 제공합니다.Future
의 확장된 버전으로,Future
가 가지고 있던 몇 가지 단점을 보완하고 추가적인 비동기 프로그래밍 기능을 제공합니다.
1. CompletableFuture
의 주요 특징
CompletableFuture
의 주요 특징비동기 작업 실행:
CompletableFuture
를 사용하면 비동기적으로 작업을 실행하고 결과를 처리할 수 있습니다.후속 작업 정의: 작업이 완료되었을 때 자동으로 수행될 후속 작업(콜백)을 정의할 수 있습니다.
조합 및 합성: 여러 비동기 작업을 결합하거나 합성할 수 있습니다.
에러 처리: 작업 중 발생하는 예외를 처리할 수 있는 방법을 제공합니다.
논블로킹(Non-blocking) 프로그래밍: 결과를 기다리지 않고 작업을 수행하며, 작업이 완료되면 콜백을 통해 결과를 처리합니다.
2. CompletableFuture
의 생성 및 비동기 실행
CompletableFuture
의 생성 및 비동기 실행2.1. CompletableFuture
생성 및 사용 예시
CompletableFuture
생성 및 사용 예시CompletableFuture
를 사용하여 간단한 비동기 작업을 실행하고 결과를 처리하는 예시입니다.
edu.ch13.future 패키지
출력 예시
코드 설명
비동기 작업 생성:
CompletableFuture.supplyAsync()
메소드를 사용하여 비동기 작업을 시작합니다.이 작업은 2초 동안 대기한 후
"작업 완료!"
라는 결과를 반환합니다.
결과 처리:
future.thenAccept(result -> {...})
를 사용하여 작업이 완료된 후 결과를 비동기적으로 처리하는 콜백을 정의합니다. 이 콜백은 작업이 완료되면 호출됩니다.
논블로킹 실행:
CompletableFuture
는 비동기적으로 실행되므로, 작업이 완료될 때까지main
메소드의 다른 코드는 계속 실행됩니다.
3. 다양한 비동기 작업 실행 방법
CompletableFuture
는 비동기 작업을 실행하기 위한 여러 메소드를 제공합니다.
runAsync(Runnable runnable)
: 결과를 반환하지 않는 비동기 작업을 실행합니다.supplyAsync(Supplier<U> supplier)
: 결과를 반환하는 비동기 작업을 실행합니다.두 메소드 모두
Executor
를 인수로 받아 스레드 풀을 지정할 수 있습니다
edu.ch13.etcCompletableFuture 패키지 - ReturnResultOrNot 클래스
4. 후속 작업 및 콜백 처리
4.1. 작업 완료 후 콜백 처리
thenApply(Function)
: 결과를 받아 새로운 결과를 반환하는 콜백을 정의합니다.thenAccept(Consumer)
: 결과를 받아 처리하지만 반환값이 없는 콜백을 정의합니다.thenRun(Runnable)
: 결과를 사용하지 않고 단순히 실행할 콜백을 정의합니다.
edu.ch13.etcCompletableFuture 패키지
출력 예시
5. 여러 작업의 조합
5.1. 두 작업 조합
thenCombine()
: 두 개의CompletableFuture
결과를 조합하여 새로운 결과를 생성합니다.
edu.ch13.etcCompletableFuture 패키지
출력 예시
5.2. 모든 작업 완료 후 처리
edu.ch13.etcCompletableFuture 패키지 - ReturnResultOrNot 클래스
allOf()
: 여러CompletableFuture
를 배열로 받아 모든 작업이 완료될 때까지 기다립니다.
6. 예외 처리
CompletableFuture
에서는 비동기 작업 중 발생하는 예외를 처리하기 위한 메소드도 제공합니다.
exceptionally(Function)
: 예외가 발생했을 때 대체 결과를 반환하거나 처리합니다.
edu.ch13.etcCompletableFuture 패키지
출력 예시
7. CompletableFuture
의 장점
CompletableFuture
의 장점비동기 작업 간의 조합: 여러 비동기 작업을 순차적으로 또는 병렬로 실행하고, 그 결과를 조합할 수 있습니다.
콜백 처리: 작업이 완료되면 자동으로 콜백이 실행되어 결과를 처리할 수 있습니다.
에러 처리: 작업 중 발생하는 예외를 유연하게 처리할 수 있습니다.
논블로킹: 작업이 완료될 때까지 대기하지 않고, 다른 작업을 계속할 수 있습니다.
8. 정리
CompletableFuture
는 자바에서 비동기 작업을 수행하고 그 결과를 처리하는 데 매우 유용한 클래스입니다.다양한 메소드를 통해 비동기 작업의 실행, 조합, 후속 작업 처리, 예외 처리 등을 할 수 있습니다.
CompletableFuture
는Future
의 한계를 보완하여 더 강력한 비동기 프로그래밍 기능을 제공합니다.
Last updated