1.(Thread)배열 1~100까지 두 쓰레드로 입력받기

이월(0216tw)·2024년 5월 13일
post-thumbnail
  • 이 포스트는 자바의 스레드를 시나리오 형식으로 공부한 코드 내용입니다.

시나리오1

사용자 A씨는 배열에 홀수를 입력하려고 한다 1,3,5,7,..99
사용자 B씨는 배열에 짝수를 입력하려고 한다. 2,4,6,8,..100
두 사용자가 동시에 배열에 데이터를 입력해서 1~100까지의 array를 출력한다.


what do you get ?

  • Thread 생성하는 3가지 문법을 알게 됩니다.

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 )


결론 : join()을 이용하면 지정한 스레드가 완료될 때까지 특정 스레드(여기서는 Main)을 일시정지 시킬 수 있다.


코드

 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));


    }
profile
#SQLD강사 #AI개발 #AI강사 #개발자 개발도 하고 강의도 하지만 고민을 제일 많이 합니다

0개의 댓글