private final ConcurrentHashMap<String, Future<?>> userTrades;
private final ExecutorService executorService;
private final Map<String, AtomicBoolean> userRunningStatus;
private final BackDataRepository backDataRepository;
private final UpbitService upbitService;
- 프로그램 실행
public void startTrading(AuthUser authUser) {
String userId = authUser.getUserId();
// 이미 실행 중인 거래 프로그램이 있는지 확인
if (userTrades.containsKey(userId)) throw new CustomException(ErrorCode.TRADING_ALREADY_GENERATE);
// 각 사용자의 running 상태 가져오기, 없으면 false로 초기화
AtomicBoolean userRunning = userRunningStatus.computeIfAbsent(userId, k -> new AtomicBoolean(false));
// 새로운 작업을 실행하고 Future로 저장
Future<?> future = executorService.submit(() -> {
try {
// 거래 프로그램 실행 전에 running 상태를 true로 변경
userRunning.set(true);
startProgram(authUser);
} catch (Exception e) {
log.info("프로그램 실행 중 오류 발생: {}", e.getMessage());
}
});
// 사용자별로 실행 상태를 userTrades에 저장
userTrades.put(userId, future);
}
- 유저 정보 가져오기
- 실행여부 확인하기
- AtomicBoolean을 사용하여 가져온 유저에 대한 실행상태 추적하기
※ 일반적인 boolean 변수는 멀티스레드 환경에서 경쟁 조건(Race Condition)이 발생할 수 있다. - executorService.submit() 를 사용하여 비동기 실행 및 Future<?>객체로 쓰레드관리
※ Future<?>는 비동기 작업(쓰레드)의 실행 결과를 나타내는 객체
- 프로그램 종료
public void stopTrading(AuthUser authUser) {
String userId = authUser.getUserId();
// 실행중인 프로그램 가져오기
Future<?> future = userTrades.remove(userId);
if (future == null) throw new CustomException(ErrorCode.TRADING_NOT_FOUND);
// 각 사용자의 running 상태 가져오기
AtomicBoolean userRunning = userRunningStatus.get(userId);
// 사용자별로 running을 false로 설정하여 while문 종료
userRunning.set(false);
// 작업 종료
try {
future.cancel(true);
log.info("{}의 거래 프로그램이 정상적으로 종료되었습니다.", userId);
} catch (Exception e) {
log.info("프로그램 종료 중 오류 발생: {}", e.getMessage());
}
}
- userTrades에서 user의 future꺼낸 후 삭제하기
- 동작상태 가져오기
- running를 false로 set하여 안전하게 종료
- future.cancel하여 프로그램 종료
- 테스트 코드
@Test
void stopTrading() throws Exception {
// given
AuthUser authUser1 = new AuthUser("user1", "nick1", "secret1", "access1");
AuthUser authUser2 = new AuthUser("user2", "nick2", "secret2", "access2");
// when & then
// 프로그램 2개 동작
executorService.execute(() -> tradingService.startTrading(authUser1));
Thread.sleep(1000);
assertThat(userTrades).hasSize(1);
executorService.execute(() -> tradingService.startTrading(authUser2));
Thread.sleep(1000);
assertThat(userTrades).hasSize(2);
assertThat(userRunningStatus.get("user1").get()).isTrue();
assertThat(userRunningStatus.get("user2").get()).isTrue();
// 프로그램1 종료(실행 1 종료 1)
tradingService.stopTrading(authUser1);
assertThat(userTrades).hasSize(1);
assertThat(userRunningStatus.get("user1").get()).isFalse();
assertThat(userRunningStatus.get("user2").get()).isTrue();
// 프로그램2 종료(실행 0 종료 2)
tradingService.stopTrading(authUser2);
assertThat(userTrades).hasSize(0);
assertThat(userRunningStatus.get("user1").get()).isFalse();
assertThat(userRunningStatus.get("user2").get()).isFalse();
}

'프로젝트 > coin-trading' 카테고리의 다른 글
5. EC2 프로그램 동작중... 앞으로의 개선방향 (0) | 2025.02.18 |
---|---|
4. 투자전략 변경 및 EC2 배포 (0) | 2025.02.10 |
3. chatGPT 연동하기(투자 판단) (0) | 2025.02.08 |
2. 업비트 매매 API 적용 (0) | 2025.02.07 |
1. 업비트 API 적용해보기 (1) | 2025.02.05 |