
시나리오1
사용자 A씨는 배열에 홀수를 입력하려고 한다 1,3,5,7,..99
사용자 B씨는 배열에 짝수를 입력하려고 한다. 2,4,6,8,..100
두 사용자가 동시에 배열에 데이터를 입력해서 1~100까지의 array를 출력한다.
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
public class ThreadTest {
static int[] arrCheck; //원본 비교를 위한 int array
@BeforeEach
public void setUp() {
arrCheck = new int[101]; //원본 비교용에 데이터 주입 0~100
for(int i = 0 ; i<101; i++) {
arrCheck[i] = i;
}
}
############# 쓰레드를 만드는 세가지 방법 ##############
//1. Thread 를 상속해서 만들기
private static class MakeByThread extends Thread {
private int[] arr ;
private int oddOrEven;
private MakeByThread(int[] arr , int oddOrEven) {
this.arr = arr;
this.oddOrEven = oddOrEven;
}
//Thread 클래스의 run 메서드 오버라이드
@Override
public void run() {
System.out.println("MakeByThread");
for(int i = oddOrEven; i<arr.length; i+=2) {
this.arr[i] = i ;
}
}
}
@Test
@DisplayName("thread 사용버전 - 배열에 1부터 100까지 쓰레드 2개로 넣기")
public void thread_사용_버전() {
int arr[] = new int[101];
MakeByThread methodA = new MakeByThread(arr , 0);
MakeByThread methodB = new MakeByThread(arr , 1);
//스레드 2개를 만들어 실행흐름을 분기시킴
methodA.start();
methodB.start();
//join을 사용해서 A , B 가 모두 끝날때까지 기다림
try {
methodA.join();
methodB.join();
Assertions.assertArrayEquals(arrCheck , arr); //동일여부 검증 (내부 값까지)
} catch(InterruptedException e) {
//예외 설정 이유 : 일시정지 상태에서 interrupt가 발생해버리면 정지가 풀려버림
//예) A는 동작이 끝나서 일시정지 중인데 interrupt가 호출되면 일시정지가 풀린다.
e.printStackTrace();
}
}
//2. Runnable 인터페이스를 구현하기
private static class MakeByRunnable implements Runnable {
private int[] arr ;
private int oddOrEven;
private MakeByRunnable(int[] arr , int oddOrEven) {
this.arr = arr;
this.oddOrEven = oddOrEven;
}
public void run() { //Runnable 의 인터페이스 메서드 구현
System.out.println("runnable 실행");
for(int i = oddOrEven; i<arr.length; i+=2) {
this.arr[i] = i ;
}
}
}
@Test
@DisplayName("Runnable 사용버전 - 배열에 1부터 100까지 쓰레드 2개로 넣기")
public void runnable_사용_버전() {
/* new Thread( Runnable 구현객체 ); */
int arr[] = new int[101];
Thread methodA = new Thread(new MakeByRunnable(arr , 0));
Thread methodB = new Thread(new MakeByRunnable(arr , 1));
methodA.start();
methodB.start();
try {
methodA.join();
methodB.join();
Assertions.assertArrayEquals(arrCheck , arr);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
//3. 람다식을 이용한 Runnable
@Test
@DisplayName("lambda Runnable 사용버전 - 배열에 1부터 100까지 쓰레드 2개로 넣기")
public void lambda_Runnable_사용_버전() {
int arr[] = new int[101];
/* 익명 클래스와 다형성 원리를 이용 */
// 익명 클래스 : Runnable 인터페이스를 구현한 구현체 클래스 자리에
// 익명으로 클래스를 만들어 바로 사용한다.
// 여기서 () -> { .. } 의 의미는 Runnable 인터페이스가 가지고 있는
// run() 메서드를 오버라이드하고 있다는 의미이다. (그래서 괄호에 파라미터 불가)
//다형성 : 해당 Runnable 의 run() 메서드를 오버라이딩 함으로써
//클래스 생성 없이 진행할 수 있다.
Runnable makeByLambdaRunnableOdd = () -> {
System.out.println("lambda runnable 실행 Odd ");
for(int i = 1; i<arr.length; i+=2) {
arr[i] = i ;
}
};
Runnable makeByLambdaRunnableEven = () -> {
System.out.println("lambda runnable 실행 Even ");
for(int i = 0; i<arr.length; i+=2) {
arr[i] = i ;
}
};
Thread methodA = new Thread(makeByLambdaRunnableOdd);
methodA.setName("A씨작업");
Thread methodB = new Thread(makeByLambdaRunnableEven);
methodB.setName("B씨작업");
methodA.start();
methodB.start();
try {
methodA.join();
methodB.join();
Assertions.assertArrayEquals(arrCheck , arr);
} catch(InterruptedException e) {
e.printStackTrace();
}
/* 이렇게도 축약할 수 있을듯
new Thread( () -> {
System.out.println("AA");
}).start();
*/
}
}
만약 Main 쓰레드는 2초 , A작업은 7초 , B작업은 3초 , C작업은 5초 걸릴때
A와 B만 join() 이 걸려있다면 어떤 순서로 처리가 될까?
[ A , B 작업에 join을 적용하지 않은 경우 ] (Main -> B -> C -> A )
[ A , B 작업에 join을 적용한 경우 ] ( B -> C -> A -> Main )
위의 경우 Main 스레드가 sleep(2000) 후 A 스레드와 B 스레드가 모두 완료될 때까지 기다리고 있다. B는 작업이 3초밖에 안걸리지만 A가 7초 걸리기 때문에 A를 기다린 후에야 Main이 종료되었다.
[ B 작업에만 join을 적용한 경우 ] (B -> Main -> C -> A )
public static void main(String[] args) { //메인쓰레드
long start = System.currentTimeMillis();
Thread A = new Thread(() -> {
try {
sleep(7000);
System.out.println("A작업완료 , 걸린시간 : " + (System.currentTimeMillis() - start));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
Thread B = new Thread(() -> {
try {
sleep(3000);
System.out.println("B작업완료 , 걸린시간 : " + (System.currentTimeMillis() - start));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
Thread C = new Thread(() -> {
try {
sleep(5000);
System.out.println("C작업완료 , 걸린시간 : " + (System.currentTimeMillis() - start));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
A.start();
B.start();
C.start();
try {
sleep(2000);
A.join(); //A쓰레드 끝날때까지 기다려
B.join(); //B쓰레드 끝날때까지 기다려
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Main 작업완료 , 걸린시간 : " + (System.currentTimeMillis() - start));
}