# ✏️ 스레드 ## zzawang 스레드의 생성자 종류를 보면 `Thread()`, `Thread(Runnable target)`, `Thread(Runnable target, String name)` 등 다양한데, 여기서 Runnable은 무엇일까? **Runnable interface**는 인스턴스가 스레드에 의해 실행되도록 모든 클래스에 의해 구현되어야 하는 interface이다. 생성자 안의 Runnable은 Runnable 인터페이스를 구현한 구현체이다. 따라서, `run()`메소드를 반드시 정의해야 한다. 우리가 RequestHandler 클래스의 `run()`메소드에서 정적인 html 파일을 응답하는 기능을 구현한 것도 이와 같은 이유이다. * 자바 스레드 API 종류([참고 문서](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html)) - start() - sleep() - run() - join() - interrupt() - 등등 ... <br> * 스레드와 동기화 만약, 우리의 시스템이 하나의 스레드만 있는 싱글 스레드 시스템이라면 공유 데이터에 접근할 때 외부 상황을 고려하지 않아도 되지만, 다중 스레드라면 공유 데이터에 접근할 때 예상하지 못한 결과가 나올 수 있다. 이러한 문제를 해결해줄 수 있는 것이 **스레드 동기화**이다. 자바에서는 자바에서는 synchronized 키워드를 통해 메서드나 코드 블록에 작성하여 스레드 동기화를 할 수 있다. 아래 코드는 synchronized 키워드를 사용하여 동기화를 하는 예시 코드이다. ```java class Money { private int myMoney = 10000; public int getMyMoney() { return myMoney; } // 메서드 전체를 임계영역으로 설정 public synchronized boolean withdraw(int money) { if (myMoney >= money) { try { Thread.sleep(1000); } catch (Exception e) { System.out.println(e); } myMoney -= money; return true; } return false; } } ``` <br><br> # ✏️ 자바의 버전별 스레드 관리 240311 ## Sid - 처음 - Runnable과 Thread를 동기화된 클래스와 메서드를 이용해 사용 - 단점: - Thread - 별도의 클래스를 만들어야 함 - 다중 상속이 불가능하므로 다른 클래스를 상속받을 수 없음 - Runnable - 값을 반환받지 못한다 - Thread, Runnable 둘 다 - 모두 지나치게 저수준의 API에 의존 - 쓰레드의 관리가 어렵다 - Java 5: - Executor, ExecutorService 인터페이스 - 스레드 실행과 태스크 제출을 분리하는 - Callable<T> 과 Future<T> - Runnable과 Thread의 변형 - Callable은 값을 반환받을 수 있음 - Future를 통해 유연하게 비동기 처리 과정을 변수에 저장, 처리 가능 - Java 7: - java.util.concurrent.RecursiveTask - 분할, 정복 알고리즘의 포크/조인 구현을 지원 - Java 8: - 스트림과 새로 추가된 람다 지원에 기반한 병렬 스트림(parallelStream()) - ForkJoinPool 프레임워크를 이용해서 병렬 처리 - CompletableFuture - 여러 비동기 작업을 조합 가능 - 예외처리 가능 - Java 9: - Flow 인터페이스 발생-구독 프로토콜을 지원하는 ## 데이 - 초기 자바에서는 Thread를 직접 상속하여 구현 후 사용하는 방식을 사용했다. - java5부터 Executors를 통해 Thread pool을 생성할 수 있게 되었고, submit()으로 Runnable을 pool에 전달하는 식으로 좀 더 추상화 수준이 높아진 방식을 사용하게 되었다. - 실행되어야 하는 태스크는 ExecutorService에 submit 되고, 큐에 들어간다. 작업 가능한 스레드가 큐에서 태스크를 가져가서 실행한다. - 이제 우리는 스레드를 직접 생성하지 않고 사용한다. pool의 생성 및 관리는 ExecutorService가 담당한다. - java7부터는 ExecutorService 기반으로 만들어진 Fork/Join 프레임워크가 도입됐다. Fork/join은 재귀적으로 더 작게 쪼개서 처리할 수 있는 태스크들을 효과적으로 처리할 수 있다. - ExecutorService와 다른 특징은 작업 빼가기(work-stealing)라는 것이다. - Thread pool에 존재하는 A라는 스레드가 현재 과부하 상태인 경우, 같은 Pool에 있는 B 스레드가 A 스레드 내부에 있는 스레드를 가져와서 처리하는 것을 의미한다. - java8에서 CompletableFuture이 등장했다. Future에서 아쉬웠던 부분들이 개선되었는데, 함수형 인터페이스의 도입과 함께 더욱 자연스럽고 이해가 쉬운 코드로 표현할 수 있게 되었으며 연산 결과를 모아서 처리하고 에러를 핸들링 할 수 있게 되었다. - Project Loom? - 가상 스레드를 사용하는 방식 - 현재 자바의 스레드는 os 스레드와 1대1로 매칭되어 있는데, 여기서의 가상 스레드는 그것을 벗어나는 가상 스레드 - https://cr.openjdk.org/~rpressler/loom/Loom-Proposal.html [참고자료](https://homoefficio.github.io/2020/12/11/Java-Concurrency-Evolution/) [참고자료(원문)](https://dzone.com/articles/java-concurrency-evolution)