코드 그라데이션

후발대 21일차 설명 추가 본문

Java/후발대

후발대 21일차 설명 추가

완벽한 장면 2023. 3. 4. 02:01

우리가 쓰레드를 만드는 방법이

1. Thread 를 상속받아 만드는 방법과

2. Runnable 이라는 인터페이스를 구현해서 만드는 방법이 있다.

 

_02_Runnable

package Prac20;

import Prac20.clean.CleanRunnable;

public class _02_Runnable {
    public static void main(String[] args) {
        CleanRunnable cleanRunnable = new CleanRunnable(); // 객체 생성
        Thread thread = new Thread(cleanRunnable);
        thread.start(); // run() 아님. CleanRunnable에서 정의되어 있는 동작을 새로운 쓰레드를 만들어서 수행하게 되는 것.

        cleanByBoss();

        // 실행을 할 때마다 순서가 조금씩 다르게 나올 수 있다. 순서를 확실하게 보장하는 건 아니기 때문이다.
    }

    // _01_Thread에서 만들었던 메서드와 동일
    public static void cleanByBoss() {
        System.out.println("--사장 청소 시작--");
        for (int i = 1; i <= 10; i+=2) {
            System.out.println("(사장) " + i +" 번방 청소 중");
        }
        System.out.println("--사장 청소 끝--");
    }
}

 

1번 방법과 2번 방법의 결정적인 차이가 무엇인가?

1번은 단일 상속만 가능. 하나의 부모 클래스만 가져야 한다.

 

2번은 인터페이스가 구현하는 것이므로 extends, implement 동시에 가능

이거 빼고는 기능적으로 동일하다.

 

 


_03_Join

package Prac20;

import Prac20.clean.CleanRunnable;

public class _03_Join {
    // 사장이 직원 고용해놓고 청소를 시키는데, 얘가 잘 하고 있는지 의심이 드는 상황.
    // 그래서 사장이 직원 끝난 후에 자기도 청소를 시작하려고 한다. **
    public static void main(String[] args) {
        CleanRunnable cleanRunnable = new CleanRunnable();
        Thread thread = new Thread(cleanRunnable);
        thread.start();

        try {
//            thread.join(); 이거 지연시간 2.5초 추가
            thread.join(2500); // try-catch로 감싸야 한다.
            // 사장이 온전히 기다렸다가 하면 직원채용의 의미가 없으니 2.5초만 참자 느낌으로 추가.
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        cleanByBoss();
    }

    //_01_Thread의 cleanByBoss와 동일.
    public static void cleanByBoss() {
        System.out.println("--사장 청소 시작--");
        for (int i = 1; i <= 10; i+=2) {
            System.out.println("(사장) " + i +" 번방 청소 중");
            try {
                Thread.sleep(1000); //지연, 이것도 try-catch 감싸야.
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("--사장 청소 끝--");
    }
}
/**
 * join()의 역할
 * 쓰레드가 시작해서 종료될 때까지 기다렸다가
 * 끝난 후 다음 메서드로 넘어가서 실행되는 것.
 */

 

CleanThread에 추가

public class CleanRunnable implements Runnable {

    @Override
    public void run() { // CleanThread에서 만들었던 메서드 그대로임
        System.out.println("--직원 청소 시작 (Runnable)~~~");
        for (int i = 2; i <= 10; i+=2) {
            System.out.println("(직원) " + i +" 번방 청소 중 (Runnable)");
        }

        // 요기
        try {
            Thread.sleep(1000); //지연, 이것도 try-catch 감싸야.
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("--직원 청소 끝 (Runnable)--");
    }
}

 

 

이러면 사장이 온전히 기다리지 않아도 된다.

 


_04_MultiThread

이번에는 Runnable 내에서 InnerClass를 이용해서 만들어보도록 하겠다.

package Prac20;

public class _04_MultiThread {
    // 말 그대로 쓰레드가 두 개 이상인 것(여러 개)
    // 사장님이 감독도 해야 하고 자기도 청소도 해야 하니까 매우 비효율성을 느낌.
    public static void main(String[] args) {

        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() { //CleanRunnable의 코드를 그대로 가져옴.
                System.out.println("--직원1 청소 시작 --"); // 1로 바꾸고, 쓸데 없는 부분 지움
                for (int i = 1; i <= 10; i+=2) { // 1번방이므로 1부터 시작
                    System.out.println("(직원1) " + i +" 번방 청소 중");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.println("--직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 =() -> { // 이건 람다식을 이용해서 만들어봄.
            System.out.println("--직원2 청소 시작 --");
            for (int i = 2; i <= 10; i+=2) {
                System.out.println("(직원2) " + i +" 번방 청소 중 ");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("--직원2 청소 끝 --");
        };

        // 두 명의 직원을 채용했으니 쓰레드 두 개 생성
        Thread cleaner1Thread = new Thread(cleaner1);
        Thread cleaner2Thread = new Thread(cleaner2);

        cleaner1Thread.start();
        cleaner2Thread.start();

    }
}

실행하면 왔다갔다 하면서 청소를 수행한다.

 

 

_05. 동기화

public class _05_Synchronization {
    public static void main(String[] args) {
//        Room room = new Room();
        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() { // MultiThread 메서드 그대로 가져오고 시간 지연 부분만 지운다. 반복문도 살짝 수정
                System.out.println("--직원1 청소 시작 --");
                for (int i = 1; i <= 10; i+=2) {
                    System.out.println("직원 1) " + i +" 번방 청소 중");
//                    room.clean("직원1");
                }
                System.out.println("--직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 =() -> {
            System.out.println("--직원2 청소 시작 --");
            for (int i = 2; i <= 10; i+=2) {
                System.out.println("직원 2) " + i +" 번방 청소 중");
//                room.clean("직원2");

            }
            System.out.println("--직원2 청소 끝 --");
        };

        Thread cleaner1Thread = new Thread(cleaner1);
        Thread cleaner2Thread = new Thread(cleaner2);

        cleaner1Thread.start();
        cleaner2Thread.start();

    }
}

 

여기서 순서대로 비어있는 방을 청소하게 만들겠다.

 

Synchronized를 다음과 같이 바꿔주고

package Prac20;

import Prac20.clean.Room;

public class _05_Synchronization {
    public static void main(String[] args) {
        Room room = new Room();
        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() { // MultiThread 메서드 그대로 가져오고 시간 지연 부분만 지운다. 반복문도 살짝 수정
                System.out.println("--직원1 청소 시작 --");
//                for (int i = 1; i <= 10; i+=2) {
                for (int i = 1; i <= 5; i++) {
//                    System.out.println("직원 1) " + i +" 번방 청소 중");
                        room.clean("직원1");
                }
                System.out.println("--직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 =() -> {
            System.out.println("--직원2 청소 시작 --");
//            for (int i = 2; i <= 10; i+=2) {
            for (int i = 1; i <= 5; i++) {
//                System.out.println("직원 2) " + i +" 번방 청소 중");
                room.clean("직원2");

            }
            System.out.println("--직원2 청소 끝 --");
        };

        Thread cleaner1Thread = new Thread(cleaner1);
        Thread cleaner2Thread = new Thread(cleaner2);

        cleaner1Thread.start();
        cleaner2Thread.start();

    }
}
/**
 * 두 쓰레드가 동시에 한 리소스에 접근하려고 하면 동시성 문제가 발생해서 똑같이 1번방이 두 번 나오는 이상한 경우도 발생한다.
 * 사실 이건 이상한 건 아니다.
 */

 

Room 클래스도 만들어준다.

public class Room {
    public int roomNumber = 1;

    // 이 싱크로나이즈드가 중요!!!
    synchronized public void clean(String name) { // 직원 누가 청소중인지 받기 위해서 name을 받음

        // ex. 직원 1 : 3번방 청소중
        System.out.println(name + ": " + roomNumber + "번방 청소중");
        roomNumber++;
    }
}

 

 

추가.

두 개 이상의 쓰레드에서 작업을 하다가 하나가 문제가 생긴다면?

-> 남은 것은 다행히 정상적으로 작동한다.

 

수정한 

_05_Synchronized

package Prac20;

import Prac20.clean.Room;

public class _05_Synchronization {
    public static void main(String[] args) {
        Room room = new Room();
        Runnable cleaner1 = new Runnable() {
            @Override
            public void run() { // MultiThread 메서드 그대로 가져오고 시간 지연 부분만 지운다. 반복문도 살짝 수정
                System.out.println("--직원1 청소 시작 --");
//                for (int i = 1; i <= 10; i+=2) {
                for (int i = 1; i <= 5; i++) {
//                    System.out.println("직원 1) " + i +" 번방 청소 중");
                     room.clean("직원1");

                     // 사고 났을 때 추가!!!
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                    if(i==2) {
                        throw new RuntimeException("못해 먹겠다~~");
                    }
                }
                System.out.println("--직원1 청소 끝 --");
            }
        };

        Runnable cleaner2 =() -> {
            System.out.println("--직원2 청소 시작 --");
//            for (int i = 2; i <= 10; i+=2) {
            for (int i = 1; i <= 5; i++) {
//                System.out.println("직원 2) " + i +" 번방 청소 중");
                room.clean("직원2");

                // 사고 났을 때 추가!!! 2222
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
            System.out.println("--직원2 청소 끝 --");
        };

        Thread cleaner1Thread = new Thread(cleaner1);
        Thread cleaner2Thread = new Thread(cleaner2);

        cleaner1Thread.start();
        cleaner2Thread.start();

    }
}

 

 

이걸 실행시켜보면

 

이렇게 나온다!

728x90

'Java > 후발대' 카테고리의 다른 글

후발대 22일차 Quiz  (0) 2023.03.07
후발대 22일차(1) 전체 코드  (0) 2023.03.07
후발대 21일차 전체 코드  (0) 2023.03.03
예외처리 Quiz  (0) 2023.03.03
후발대 20일차 설명 추가  (0) 2023.03.02
Comments