컴공생의 발자취

[내일배움캠프 10일차 TIL] 쓰레드 본문

🤝 활동/스파르타코딩클럽

[내일배움캠프 10일차 TIL] 쓰레드

MNY 2024. 4. 26. 20:18
728x90
반응형
오늘의 진도 : Java 문법 종합반 5-9 Lock&Condition까지...

오늘도 내가 강의를 들으면서 새롭게 알게된 것 그 동안 애매했던 것을 오늘의 학습으로 정리하려고 한다.
오늘의 학습, 오늘의/지난 날의 궁금증, 숙제, 특강, 코드카타, 오늘의 회고 이렇게 6개의 큰 틀로 나누어 정리할 것이다.

 

💡 오늘의 학습 키워드

- 5주차 -
프로세스 vs 쓰레드
프로세스 구조
멀티 쓰레드
쓰레드 구현 방법
데몬 쓰레드 & 사용자 쓰레드
우선순위 & 쓰레드 그룹
interrupt & sleep
join & yield & synchronized
wait & notify
Lock & Condition


 

 

프로세스 vs 쓰레드

  • 프로세스 : 운영체제로부터 자원을 할당받는 작업의 단위
  • 쓰레드 : 프로세스가 할당받은 자원을 이용하는 실행의 단위

 

프로세스 구조

: 각 프로그램은 프로세스를 통해 Code, Data, Memory(Stack, Heap)를 OS로부터 할당받는다.

  1. Code : Java main 메소드와 같은 코드
  2. Data : 프로그램이 실행 중 저장할 수 있는 저장공간(초기화된 데이터를 저장하는 공간)
  3. 메모리 영역
    - stack : 지역변수, 매개변수 리턴 변수를 저장하는 공간
    - Heap : 프로그램이 동적으로 필요한 변수를 저장하는 공간(new(), mallock())

 

멀티 쓰레드

  • 장점
    • 스택을 제외한 모든 영역 메모리 공유로 효율적
    • 비동기
  • 단점
    • 동기화 문제 발생
    • 교착 상태(데드락) 발생 : 서로 작업이 끝나길 기다리는 상태가 무한히 지속되는 현상

 

쓰레드 구현 방법

  1. Thread Class를 이용하는 것(상속)
  2. Runnable 사용 : 많이 사용
    public class Main {
    	public static void main(String[] args) {
        	Runnable runnable = new TestRunnable();
            Thread thread = new Thread(runnable);
            thread.start();
        }
    }
    
    public class TestRunnable implements Runnable {
        @Override
        public void run() {
            // 쓰레드에서 수행할 작업 정의!
            for (int i = 0; i < 100; i++) {
                System.out.print("$");
            }
        }
    }​
  3. 람다식 이용 : 많이 사용
    Runnable task = () -> {
        int sum = 0;
        for (int i = 1; i <= 50; i++) {
            sum += 1;
            System.out.println(sum);
        }
        System.out.println(Thread.currentThread().getName() + "최종 합 : " + sum) ;
    };
    
    Thread thread1 = new Thread(task);
    thread1.setName("thread1");
    
    thread1.start();​

 

데몬 쓰레드 & 사용자 쓰레드

  • 데몬 쓰레드 : 보이지 않는 곳 실행 / 낮은 선순위 / 상대적으로 적은 리소스 할당 
    ex) 가비지 컬렉터(GC)
Thread thread = new Thread(demon);
thread.setDaemon(true); // true로 설정시 데몬스레드로 실행됨
  • 사용자 쓰레드 : 보이는 곳 / 높은 우선순위
    ex) 메인 쓰레드

# 사용자 쓰레드의 작업이 끝나면 데몬 쓰레드도 자동으로 종료

 

우선순위 & 쓰레드 그룹

  • 우선순위
Thread thread1 = new Thread(task1);
thread1.setPriority(8); // 우선순위 설정
int threadPriority = thread1.getPriority(); // 우선순위 반환
  • 쓰레드 그룹 : 모든 쓰레드는 반드시 하나의 그룹에 포함되어야 한다. 
// ThreadGroup 클래스로 객체 만들기
ThreadGroup group1 = new ThreadGroup("Group1");
// Thread 객체 생성시 첫번째 매개변수로
// Thread(ThreadGroup group, Runnable target, String name)
Thread thread1 = new Thread(group1, task, "Thread 1");
// Thread에 ThreadGroup 할당 확인
thread1.getThreadGroup().getName();

 

interrupt & sleep

  • interrupt : 일시정지 상태인 쓰레드를 실행대기 상태로 만듦
thread.interrupt(); // 또는 group.interrupt()
Thread.currentThread().isInterrupted(); // 현재 쓰레드가 인터럽트되었는지 확인
  • sleep : 지정된 시간동안 멈추기(실행 -> 일시정지)
    - catch(exception)과 함께 사용
Thread.sleep()

 

join & yield & synchronized

  • join : 정해진 시간동안 지정한 쓰레드가 작업하는 것을 기다림
Thread thread = new Thread(task, "thread");

thread.start();

try {
    thread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • yield : 남은 시간을 다음 쓰레드에게 양보하고 자신은 실행대기 상태가 됨
더보기
public class Main {
    public static void main(String[] args) {
        Runnable task = () -> {
            try {
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName());
                }
            } catch (InterruptedException e) {
                Thread.yield();
            }
        };

        Thread thread1 = new Thread(task, "thread1");
        Thread thread2 = new Thread(task, "thread2");

        thread1.start();
        thread2.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread1.interrupt();

    }
}
  • synchronized : 멀티쓰레드에서 동기화
    • 실행할 메서드 앞
      public synchronized void asyncSum() {
      	  ...침범을 막아야하는 코드...
      }​
    • 실행할 코드 묶음 앞
      synchronized(해당 객체의 참조변수) {
      		...침범을 막아야하는 코드...
      }​

 

wait & notify

  • 사용 예제(더보기 ..Click)
더보기
public class Main {
    public static String[] itemList = {
            "MacBook", "IPhone", "AirPods", "iMac", "Mac mini"
    };
    public static AppleStore appleStore = new AppleStore();
    public static final int MAX_ITEM = 5;

    public static void main(String[] args) {

        // 가게 점원
        Runnable StoreClerk = () -> {
                while (true) {
                    int randomItem = (int) (Math.random() * MAX_ITEM);
                    appleStore.restock(itemList[randomItem]);
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ignored) {
                    }
                }
        };

        // 고객
        Runnable Customer = () -> {
                while (true) {
                    try {
                        Thread.sleep(77);
                    } catch (InterruptedException ignored) {
                    }

                    int randomItem = (int) (Math.random() * MAX_ITEM);
                    appleStore.sale(itemList[randomItem]);
                    System.out.println(Thread.currentThread().getName() + " Purchase Item " + itemList[randomItem]);
                }
        };


        new Thread(StoreClerk, "StoreClerk").start();
        new Thread(Customer, "Customer1").start();
        new Thread(Customer, "Customer2").start();

    }
}

class AppleStore {
    private List<String> inventory = new ArrayList<>();

    public void restock(String item) {
        synchronized (this) {
            while (inventory.size() >= Main.MAX_ITEM) {
                System.out.println(Thread.currentThread().getName() + " Waiting!");
                try {
                    wait(); // 재고가 꽉 차있어서 재입고하지 않고 기다리는 중!
                    Thread.sleep(333);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 재입고
            inventory.add(item);
            notify(); // 재입고 되었음을 고객에게 알려주기
            System.out.println("Inventory 현황: " + inventory.toString());
        }
    }

    public synchronized void sale(String itemName) {
        while (inventory.size() == 0) {
            System.out.println(Thread.currentThread().getName() + " Waiting!");
            try {
                wait(); // 재고가 없기 때문에 고객 대기중
                Thread.sleep(333);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        while (true) {
            // 고객이 주문한 제품이 있는지 확인
            for (int i = 0; i < inventory.size(); i++) {
                if (itemName.equals(inventory.get(i))) {
                    inventory.remove(itemName);
                    notify(); // 제품 하나 팔렸으니 재입고 하라고 알려주기
                    return; // 메서드 종료
                }
            }

            // 고객이 찾는 제품이 없을 경우
            try {
                System.out.println(Thread.currentThread().getName() + " Waiting!");
                wait();
                Thread.sleep(333);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

 

  • wait : 실행 중이던 쓰레드는 해당 객체의 대기실(waiting pool)에서 통지를 기다린다
  • notify : 해당 객체의 대기실(waiting pool)에 있는 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받는다.

 

Lock & Condition

  • Lock
    • ReentrantLock
      • 가장 일반적인 Lock
      • 특정 조건에서 Lock 풀고, 나중에 다시 Lock을 얻어 임계영역으로 진입
        public class MyClass {
            private Object lock1 = new Object();
            private Object lock2 = new Object();
            
            public void methodA() {
                synchronized (lock1) {
                    methodB();
                }
            }
            
            public void methodB() {
                synchronized (lock2) {
                    // do something
                    methodA();
                }
            }
        }​
    • ReentrantReadLock
      • 읽기(공유)/쓰기(베타적) Lock 별도 제공
      • ReadOnlyLock
      • 데이터 변경 방지 : 읽기Lock -> 쓰기Loc(허용X)
    • StampedLock : 데이터 변경 전에 락을 걸지 않는 것(낙관적인 Lock )
  • Condition : waiting pool 내 쓰레드를 구분하지 못한다는 문제 해결
    • await() : wait()와 비슷
    • signal() : notify()와 비슷
    • 사용 예제 (더보기 ..Click)
      더보기
      public class Main {
      public static final int MAX_TASK = 5;
      
      private ReentrantLock lock = new ReentrantLock();
      
      // lock으로 condition 생성
      private Condition condition1 = lock.newCondition();
      private Condition condition2 = lock.newCondition();
      
      private ArrayList<String> tasks = new ArrayList<>();
      
      // 작업 메서드
      public void addMethod(String task) {
      			lock.lock(); // 임계영역 시작
      	
      			try {
      				while(tasks.size() >= MAX_TASK) {
      						String name = Thread.currentThread().getName();
      						System.out.println(name+" is waiting.");
      						try {
      							condition1.await(); // wait(); condition1 쓰레드를 기다리게 합니다.
      							Thread.sleep(500);
      						} catch(InterruptedException e) {}	
      				}
      	
      				tasks.add(task);
      				condition2.signal(); // notify();  기다리고 있는 condition2를 깨워줍니다.
      				System.out.println("Tasks:" + tasks.toString());
      			} finally {
      				lock.unlock(); // 임계영역 끝
      			}
      		}
      	}​

 


지난 날의 궁금증

  • Q : 원시 타입이 뭐지?
    • A : 논리형, 문자형, 정수형, 실수형 등의 실제 데이터(값)를 저장하는 타입
  • Q : generic에는 wrapper 클래스만 들어가나?
    • A : wrapper 클래스만 사용 가능! //  이거 당연한 질문인데..? 뭔가 다른 의미의 질문이었던 것 같은데...
  • Q : arrayList vs linkedList?
    • A : ArrayList - 배열 기반으로 빠른 임의 접근이 가능하지만, 요소의 추가/삭제가 느릴 수 있고, 메모리 사용이 유연하다. LinkedList - 요소의 추가/삭제가 빠르지만 임의 접근이 느리며, 메모리 사용이 많을 수 있다.

 

  • 아직 찾아보지 못 한 궁금증(더보기 ..Click!)
더보기
  • Q : 참조는 reference인데 자바는 call by value로만 동작하는 것 아닌가?
  • Q : wrapper클래스.. 그래서 무슨 기능들을 가지고 있는데?
  • Q : Object.equals와 str.equals의 차이? // 요건 공식 문서를 찾아봐야겠어..

 


숙제

  • 내가 만들지 못한 부분
    • Main
    • Parser : parseFirstNum, parseSecondNum, parseOperator
  • 만들지 못한 이유에 대한 고촬

함수 고정이고 throws을 붙이지 않고 구현해야하는 줄 알았다.. 
함수 밑에 구현이라고만 되어있어서 변경하지 않고 해야하는 줄 알고 아? 이거 어떻게 throws 붙이지 않고 해? 라고 생각했다. 또, throws Exception이 아니라 우리가 만들어낸 throws BadInputException으로 해야하는 줄 알았는데..
-> throw & throws에 대한 이해 부족, try ~ catch의 활용력 부족

 


알고리즘 심화 특강

시간 복잡도
O(1) < O(logn) < O(n) < O(n longn) < O(n^2) < O(n!)

 


코드 카타 

* 프로그래머스로 진행

 

  • 알고리즘 : 숫자 비교하기
  • SQL : 동물 수 구하기

 


 

오늘의 회고

  • 12시간 중 얼마나 몰입했는가?

집중도는 어제랑 비슷했던 것 같다.

오늘따라 너무 피곤했다. 그래서 졸면서 듣느라구 예정했던 5주차 강의를 모두 듣지 못했다.

 

  • 오늘의 생각

예외처리를 어떻게 활용할지 try ~ catch를 어디에서 사용할지에 대해 응용력이 많이 부족한 것 같다.

그리고 먹는 걸 최소한으로 줄여야 하나..? 식곤증이 심하다.

내일 정처기가 어떻게 될지 살짝 걱정된다. 준비를 거의 못해서ㅠ

 

  • 내일 학습할 것은 무엇인지

정처기 시험 치고 5주차 강의 마무리 하고 부족한 부분(상속, 생성자, Getter&Setter, 예외처리, try~catch)를 다시 복습할 예정이다. 시간이 되면 5주차 쓰레드도 다시 한 번 복습하면서 정리하면 좋을 것 같다.

그러고 개인과제 내용을 한 번 살펴본 후 개인과제용 레파지토리를 만들 예정이다.

 

728x90
반응형