# ✏️ 스레드
## 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)