[TIL]유데미 자바 스레드 조정 등
스레드의 실행 제어
스레드
프로그램에서 독립적으로 실행되는 작은 작업 단위임
한 번 시작된 스레드는
다른 스레드와 독립적으로
실행실행
순서를 예측할 수 없음
스레드 간
의존 관계
가 있는 경우이를
제어
해야 안전한 결과를 얻을 수 있음.
스레드 의존성과 효율
의존하는 스레드 문제
스레드 A
의 결과가스레드 B
의 입력으로 필요한 경우스레드 B는 스레드 A 가 작업을 완료했는지 알아야 함.
스레드 B 가 계속적으로 스레드 A 가 작업을 완료했는지 확인하는 방법은
엄청
비효율
better
스레드 B 를
대기 상태
로 두고, 스레드 A가 완료되는 경우 다시깨어나게
한다.Thread.join
활용
예시
package com.thread.coordination.join;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
/**
* packageName : com.thread.coordination.join
* fileName : Mian
* author : ipeac
* date : 24. 12. 19.
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 24. 12. 19. ipeac 최초 생성
*/
public class Main {
public static void main(String[] args) {
List<Long> inputNumbers = List.of(0L, 3435L, 2324L, 554L, 34L, 23525L, 542L, 245L, 245L);
List<FactorialThread> threads = new ArrayList<>();
for (Long inputNumber : inputNumbers) {
threads.add(new FactorialThread(inputNumber));
}
for (Thread thread : threads) {
thread.start();
}
for (int i = 0; i < inputNumbers.size(); i++) {
FactorialThread thread = threads.get(i);
if (thread.isFinished()) {
System.out.println("Factorial of " + inputNumbers.get(i) + " is " + thread.getResult());
} else {
System.out.println("The calculation for the factorial of " + inputNumbers.get(i) + " is still in progress.");
}
}
}
public static class FactorialThread extends Thread {
private long inputNumber;
private BigInteger result = BigInteger.ZERO;
private boolean isFinished = false;
public FactorialThread(long inputNumber) {
this.inputNumber = inputNumber;
}
@Override
public void run() {
this.result = factorial(inputNumber);
this.isFinished = true;
}
public BigInteger factorial(long n) {
BigInteger tempResult = BigInteger.ONE;
for (long i = n; i > 0; i--) {
tempResult = tempResult.multiply(new BigInteger(Long.toString(i)));
}
return tempResult;
}
public boolean isFinished() {
return isFinished;
}
public BigInteger getResult() {
return result;
}
}
}
//
Factorial of 0 is 1
The calculation for the factorial of 3435 is still in progress.
The calculation for the factorial of 2324 is still in progress.
Factorial of 554 is //생략
Factorial of 34 is 295232799039604140847618609643520000000
The calculation for the factorial of 23525 is still in progress.
Factorial of 542 is //길어서 생략
Factorial of 245 is 3446381088548184667326770790875951922006976951375582384611003611981639529782351868763804143237672379379846868628577527599609043944010849343355183826916579274876093562728501050027821075609832035303654996749361753195163012769070700485723141551669720900953728011558584003035375928212655846940281367061125645619508966404523085638743763256980870494465156859819224514274661087972929242817912092409671474491631797677547338910924800000000000000000000000000000000000000000000000000000000000
Factorial of 245 is 3446381088548184667326770790875951922006976951375582384611003611981639529782351868763804143237672379379846868628577527599609043944010849343355183826916579274876093562728501050027821075609832035303654996749361753195163012769070700485723141551669720900953728011558584003035375928212655846940281367061125645619508966404523085638743763256980870494465156859819224514274661087972929242817912092409671474491631797677547338910924800000000000000000000000000000000000000000000000000000000000
해당 코드에서 메인 스레드 - 여럿 계산 스레드가 존재한다.
계산 스레드같은 경우 run() 으로 메인 스레드와 별도로 돌아감.
메인 스레드는 계산 스레드가 작업이 완료되었는지까지 기다려주지 않기에
거의 조금 계산이 길어지는 경우
still in progress 가 뜨면서
계산중임을 알려준다..
스레드가 끝날때까지 기다리는 Thread.join() 이 필요하다.
Thread.join() 적용
package com.thread.coordination.join;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws InterruptedException {
List<Long> inputNumbers = List.of(0L, 3435L, 2324L, 554L, 34L, 23525L, 542L, 245L, 245L);
List<FactorialThread> threads = new ArrayList<>();
for (Long inputNumber : inputNumbers) {
threads.add(new FactorialThread(inputNumber));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
for (int i = 0; i < inputNumbers.size(); i++) {
FactorialThread thread = threads.get(i);
if (thread.isFinished()) {
System.out.println("Factorial of " + inputNumbers.get(i) + " is " + thread.getResult());
} else {
System.out.println("The calculation for the factorial of " + inputNumbers.get(i) + " is still in progress.");
}
}
}
public static class FactorialThread extends Thread {
private long inputNumber;
private BigInteger result = BigInteger.ZERO;
private boolean isFinished = false;
public FactorialThread(long inputNumber) {
this.inputNumber = inputNumber;
}
@Override
public void run() {
this.result = factorial(inputNumber);
this.isFinished = true;
}
public BigInteger factorial(long n) {
BigInteger tempResult = BigInteger.ONE;
for (long i = n; i > 0; i--) {
tempResult = tempResult.multiply(new BigInteger(Long.toString(i)));
}
return tempResult;
}
public boolean isFinished() {
return isFinished;
}
public BigInteger getResult() {
return result;
}
}
}
for (Thread thread : threads) {
thread.join();
}
을 통하여 메인 스레드와 계산 스레드를 연결한다.
메인 스레드는 계산 스레드의 계산이 병행적으로 모두 완료된 경우에
동작할 것이다
다만,
주의해야할 케이스가 있다.
List<Long> inputNumbers = List.of(100000000000L, 3435L, 2324L, 554L, 34L, 23525L, 542L, 245L, 245L);
에서
100000000000L
의 값에 대해계산 스레드가 수행하는 경우
왠종일 계산하고 쳐 앉아있다.
그래서 해당 스레드마다 타임아웃을 어느정돈 둬야한다.
Duration 옵션을 줄 수 있느데
해당 위치에 2000 을 적으면 2초 동안 계산이 이루어지지 않으면 해당 스레드는 기다리지 않겠다는 의미임
결과
The calculation for the factorial of 100000000000 is still in progress. Factorial of 3435 is // 길어서 생략 Factorial of 2324 is // 길어서 생략 Factorial of 554 is 1191727136806837244882582283106805387770005755965908047983081245960480213330563853528955253930547421215039291311599076798092426516053875895821245375462369728252493061843487510963049523488888754539791837452943408399859647327147502547052379636286273055010465806029110009104713085157860683741002669049888655075983983852711560875259367330433663761130741971283507845930972311843631259051730384176605689209336690194461503742469785985047389464131210379943162027387213523218050067026223431883424750837023717093059254089678160619956112022737746587181813173857534345979459157068479550808846993437317436702137496149440224189233486485246017846204920360701166257511560201387840137612887313560409842277481196061085397629797577251875676163423104849316410147126590756098226138000160385539304989876897873673358364697848479376024982124602315498644546485373020148670929575725115041050733030044378143663458513596302213255150637965270579688496576332377878020448108210609176193410494935339163990414001675136639483740017525779300407406500286260875156438834048234394302618852929817122189693614338599864305049618766373265235382830808586093265282193823184945426913474641920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Factorial of 34 is 295232799039604140847618609643520000000 Factorial of 23525 is //너무길어서 생략 Factorial of 542 is 1607535095378011891056328484225529595243838798499054994766025156939288169810835272449306354580237969682411561904659247352233869578500369085015849202525200176033722982248939071551000445386157616140174101436646697998310453314131718849668026597339962830038313202330305128176688853506387490682735113517976086814097483502994870323946561908276072060979277468132673766174329569237354871039018414225133977033767828902564327303205632246854879683635732298203740589425046261382371639739486392287792345081093595873758904816605811552325582679996192624783892220035029850790339575675172367310968287156079757464668269000854651621014536329222770032994629808829842306684806739303129202118931252425617709020165318344315725608628457669138904568258434726321656634035862803596978425264476046798283242280066301566642768649189395076269718280978615189118280004812666753362943638722532280788300931624201743200983787393440774364471798212114625772668425190618929367461976989738835188658583210597078950867099896465084703122130411827593805066916468242618448796850243328574235699250768859321062302677755711983880776617227228201878615112377392168960000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Factorial of 245 is 3446381088548184667326770790875951922006976951375582384611003611981639529782351868763804143237672379379846868628577527599609043944010849343355183826916579274876093562728501050027821075609832035303654996749361753195163012769070700485723141551669720900953728011558584003035375928212655846940281367061125645619508966404523085638743763256980870494465156859819224514274661087972929242817912092409671474491631797677547338910924800000000000000000000000000000000000000000000000000000000000 Factorial of 245 is 3446381088548184667326770790875951922006976951375582384611003611981639529782351868763804143237672379379846868628577527599609043944010849343355183826916579274876093562728501050027821075609832035303654996749361753195163012769070700485723141551669720900953728011558584003035375928212655846940281367061125645619508966404523085638743763256980870494465156859819224514274661087972929242817912092409671474491631797677547338910924800000000000000000000000000000000000000000000000000000000000
다만! 어플리케이션이 종료되지 않는 문제가 발생한다
이는 계산 스레드 하나가 아직 완료가 되지 않고 종료되지 않아서 발생하는 문제이다.
어플리케이션은 스레드가 하나라도 살아있는 경우 동작하지 않음을 알아야 한다.
종료를 시키려면 어떻게 해야할까?
바로
데몬 스레드
로 계산 스레드들을 설정하는 것이다데몬 스레드는 프로그램이 종료되는 경우 자동으로 종료되는 백그라운드 스레드이다.
public static void main(String[] args) throws InterruptedException {
List<Long> inputNumbers = List.of(100000000000L, 3435L, 2324L, 554L, 34L, 23525L, 542L, 245L, 245L);
List<FactorialThread> threads = new ArrayList<>();
for (Long inputNumber : inputNumbers) {
threads.add(new FactorialThread(inputNumber));
}
for (Thread thread : threads) {
thread.setDaemon(true); // 데몬으로 설정
thread.start();
}
for (Thread thread : threads) {
thread.join(2000);
}
for (int i = 0; i < inputNumbers.size(); i++) {
FactorialThread thread = threads.get(i);
if (thread.isFinished()) {
System.out.println("Factorial of " + inputNumbers.get(i) + " is " + thread.getResult());
} else {
System.out.println("The calculation for the factorial of " + inputNumbers.get(i) + " is still in progress.");
}
}
}
기억해야할 점
스레드 실행 순서는 제어가 불가능하다
스레드 간의 의존 관계가 있는 경우 항상 이를 제어할 필요성이 있다
thread.join 으로 기다리는 시간을 설정해 프로그램이 멈추지 않게 해야 한다
방어적으로 프로그래밍하라.
실습
코딩 연습 2: 멀티스레딩 계산
이번 코딩 연습에서는 지난 강의에서 배운 모든 지식을 활용해 보겠습니다.
연습을 시작하기 전에, 특히 다음 주제를 복습해주시기 바랍니다.
스레드 생성 -
Thread
클래스와start()
메서드를 이용해 스레드를 생성하고 시작하는 방법입니다.스레드 조인 -
Thread.join()
메서드를 활용해 다른 스레드를 기다리는 방법입니다.이번 연습에서는 다음 식을 효율적으로 계산해 보겠습니다.
result = base1 ^ power1 + base2 ^ power2
여기서
a^b
는 a를 b 제곱했다는 뜻입니다.예를 들어,
10^2 = 100
입니다.숫자를 거듭제곱을 하는 작업은 복잡한 연산이므로, 다음 식을 실행하려 합니다.
result1 = x1 ^ y1
result2 = x2 ^ y2
둘을 동시에 실행하고,
마지막에는 다음과 같이 결과값을 더합니다.
result = result1 + result2
이렇게 하면 전반적인 계산 속도를 더 빠르게 할 수 있습니다.
참고 :
base1 >= 0, base2 >= 0, power1 >= 0, power2 >= 0
import java.math.BigInteger;
public class ComplexCalculation {
public BigInteger calculateResult(BigInteger base1, BigInteger power1, BigInteger base2, BigInteger power2) {
BigInteger result;
/*
Calculate result = ( base1 ^ power1 ) + (base2 ^ power2).
Where each calculation in (..) is calculated on a different thread
*/
return result;
}
private static class PowerCalculatingThread extends Thread {
private BigInteger result = BigInteger.ONE;
private BigInteger base;
private BigInteger power;
public PowerCalculatingThread(BigInteger base, BigInteger power) {
this.base = base;
this.power = power;
}
@Override
public void run() {
/*
Implement the calculation of result = base ^ power
*/
}
public BigInteger getResult() { return result; }
}
}
위는 베이스 코드임.
아래 내 제출
package com.thread.coordination.join;
import java.math.BigInteger;
import java.util.List;
public class ComplexCalculation {
public BigInteger calculateResult(BigInteger base1, BigInteger power1, BigInteger base2, BigInteger power2) {
BigInteger result;
/*
Calculate result = ( base1 ^ power1 ) + (base2 ^ power2).
Where each calculation in (..) is calculated on a different thread
*/
List<Thread> threads = List.of(
new PowerCalculatingThread(base1, power1),
new PowerCalculatingThread(base2, power2)
);
threads.forEach(thread -> { // 데몬 스레드로 만들고
thread.setDaemon(true);
thread.start();
});
for (Thread thread : threads) {
try {
thread.join(); // 스레드 메인스레드에 연결
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//계산
result = ((PowerCalculatingThread) threads.get(0)).getResult().add(((PowerCalculatingThread) threads.get(1)).getResult());
return result;
}
private static class PowerCalculatingThread extends Thread {
private BigInteger result = BigInteger.ONE;
private final BigInteger base;
private final BigInteger power;
public PowerCalculatingThread(BigInteger base, BigInteger power) {
this.base = base;
this.power = power;
}
@Override
public void run() {
this.calculateResult();
}
private void calculateResult() {
this.result = this.base.pow(this.power.intValue());
}
public BigInteger getResult() {
return result;
}
}
}