문제
Java_Game 프로젝트를 진행하던 와중 던전 기능중 몬스터와 유저 사이에서 싸우는 것에 멀티스레드로 코드를 작성할려고 하였다. 하지만 코드를 작성하고 오류가 많아지고 어떻게 해결을 해야 할까 고민하였다. 결국에는 해결을 하였지만 아직 나에게는 멀티스레드에 대해 자세히 모르는것 같아서 공부를 시작하였다.
공부 방식
1번째, 멀티스레드에 대해 찾아본 결과 동기 와 비동기을 찾게 되었다. ( 알고는 있었다. ) 그래서 나는 내가 아는 정보토대로 적어 보자면,
- 동기 : 동기식 방식은 순서대로 진행하다가 다른 객체를 실행할때 다른 객체가 끝나야 본 객체가 다시 실행이 되는 방식이다. ( 직렬 )
- 비동기 : 비동기식 방식은 순서대로 진행하다가 다른 객체를 실행하고 본 객체도 같이 실행하는 방식이다. ( 병렬 )
라는 내용으로 이해를 하고 있었고 찾아본 결과 내가 알고 있는 내용이 맞았다. 더 자세히 공부해보면
동기 ? 비동기 ?
동기
비동기
위의 그림으로 설명할수 있었다.
좀더 공부하면, 스레드풀 (Thread Pool)
- 스레드 풀(Thread Pool)은 작업 처리를 위해 여러 개의 스레드를 미리 생성해 두고, 작업 큐(Queue)에 들어오는 작업들을 이 스레드들이 처리하는 디자인 패턴입니다.스레드 풀은 일반적으로 Executor 인터페이스를 구현한 클래스를 사용하여 생성됩니다. Executor 인터페이스는 execute() 메서드를 가지고 있으며, 작업을 처리하기 위해 스레드를 할당합니다. ExecutorService는 Executor 인터페이스를 확장한 인터페이스로, 스레드 풀을 관리하기 위한 메서드를 추가로 가지고 있습니다.
- 자바에서는 ThreadPoolExecutor 클래스를 사용하여 스레드 풀을 생성합니다. ThreadPoolExecutor 클래스는 스레드 풀의 크기, 큐의 크기, 스레드 생성 방법 등 다양한 설정을 제공합니다. 스레드 풀을 사용하면 일정한 개수의 스레드를 유지하면서 비동기 작업을 처리할 수 있으며, 효율적인 자원 관리를 할 수 있습니다.
- 일반적으로 스레드를 생성하고 삭제하는 비용은 상당히 큽니다. 이러한 스레드 생성 비용과 스레드를 관리하는 부담을 줄이기 위해 스레드 풀을 사용합니다. 스레드 풀을 사용하면 스레드를 재사용할 수 있으며, 스레드 생성 및 삭제 비용을 줄일 수 있습니다.
- Notion 에 정리한 글 -
스레드 풀 이라는 디자인 패턴을 알게 되었고, 스레드 풀에 쓰는 ThreadPoolExecutor 클래스 대해 더 공부하기 시작하였다. ThreadPoolExecutor 클래스에 생성자는 이렇게 되어 있었다.
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
- corePoolSize : 스레드 풀의 초기 스레드 개수
- maximumPoolSize : 스레드 풀의 최대 스레드 개수
- keepAliveTime : 코어 스레드 이외의 스레드들이 대기하는 시간
- unit : keepAliveTime의 단위
- workQueue : 작업 큐
- threadFactory : 스레드 생성에 사용되는 팩토리
- handler : 작업이 거부될 때 실행할 핸들러
여기서 TimeUnit, BlockingQueue<Runnable>, ThreadFactory, RejectedExecutionHandler 잘 몰라서 검색을 찾아 봤다.
TimeUnit 이넘 클래스
TimeUnit 클래스는 자바에서 제공하는 시간 단위를 나타내는 열거형(Enum) 클래스입니다. 이 클래스는 시간 단위 간의 변환과 대기 시간을 지정할 때 사용됩니다. TimeUnit 클래스는 다음과 같은 시간 단위를 제공합니다.
- TimeUnit .NANOSECONDS : 나노초
- TimeUnit .MICROSECONDS : 마이크로초
- TimeUnit .MILLISECONDS : 밀리초
- TimeUnit .SECONDS : 초
- TimeUnit .MINUTES : 분
- TimeUnit .HOURS : 시간
- TimeUnit .DAYS : 일
BlockingQueue<Runnable> 클래스
BlockingQueue<Runnable> 클래스는 다음과 같은 메서드를 제공합니다.
- add(Runnable r) : 지정된 요소를 큐에 추가합니다. 이 작업이 성공하면 true를 반환하고, 큐가 가득 차서 추가에 실패하면 IllegalStateException 예외를 발생시킵니다.
- offer(Runnable r) : 지정된 요소를 큐에 추가합니다. 이 작업이 성공하면 true를 반환하고, 큐가 가득 차서 추가에 실패하면 false를 반환합니다.
- put(Runnable r) : 지정된 요소를 큐에 추가합니다. 큐가 가득 차서 요소를 추가할 수 없으면 이 메서드는 스레드를 블록합니다.
- take() : 큐에서 요소를 제거하고 반환합니다. 큐가 비어있으면 이 메서드는 스레드를 블록합니다.
- poll(long timeout, TimeUnit unit) : 지정된 시간 동안 큐에서 요소를 제거하고 반환합니다. 만약 지정된 시간 동안 큐에서 요소를 가져올 수 없으면 null을 반환합니다.
- new ArrayBlockingQueue<Runnable>(2) : 크기가 2인 큐(Queue) 를 생성한다.
ThreadFactory 인터페이스
ThreadFactory는 새로운 스레드를 생성하는 객체입니다. 스레드를 생성할 때마다 ThreadFactory 객체를 사용하여 스레드를 생성하면, 생성된 스레드의 이름, 우선순위, 그룹 등을 쉽게 지정할 수 있습니다.
public interface ThreadFactory{
Thread newThread(Runnable r);
}
ThreadFactory를 사용하면, ExecutorService 인터페이스의 newFixedThreadPool(), newCachedThreadPool() 등의 메서드에서 스레드를 생성할 때 ThreadFactory 객체를 지정할 수 있습니다. 이렇게 지정한 ThreadFactory 객체는 해당 ExecutorService 객체가 생성하는 모든 스레드에 적용됩니다.
RejectedExecutionHandler 클래스
RejectedExecutionHandler는 ExecutorService가 처리할 수 있는 작업의 수를 초과했을 때 발생하는 예외 상황을 처리하기 위한 인터페이스입니다.
rejectedExecution(Runnable r, ThreadPoolExecutor executor) : ExecutorService가 처리할 수 있는 작업의 수를 초과했을 때 호출되는 메서드입니다. 이 메서드에서는 작업을 어떻게 처리할 지를 결정합니다. 예를 들어, 작업을 다시 큐에 넣거나, 예외를 발생시키거나, 다른 작업으로 대체할 수 있습니다.
ThreadPoolExecutor 구현
ExecutorService executor = new ThreadPoolExecutor(
2,
4,
10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
던전 기능 문제해결 자세한 내용
'TIL' 카테고리의 다른 글
Spring Security AuthenticationProvider / BCryptPasswordEncoder match 에러 (2) | 2023.06.07 |
---|---|
RedisConfig 에서 오류 발생. (0) | 2023.06.01 |
동기와 비동기 방식 / 스레드 풀 (0) | 2023.04.27 |
Java_Game Project / Multi Thread ( 멀티스레드 ) (0) | 2023.04.20 |
개발 블로그 시작... (0) | 2023.04.18 |