Skip to content

[사전과제] 애플리케이션 레벨에서 동시성 제어#2

Open
futuremaker019 wants to merge 4 commits intohanghae-skillup:mainfrom
futuremaker-projects:dev
Open

[사전과제] 애플리케이션 레벨에서 동시성 제어#2
futuremaker019 wants to merge 4 commits intohanghae-skillup:mainfrom
futuremaker-projects:dev

Conversation

@futuremaker019
Copy link

@futuremaker019 futuremaker019 commented Jan 1, 2025

작업 내용

애플리케이션 레벨에서 적용할 수 있는 4가지 방법으로 동시성을 제어했습니다.

  1. synchronized 사용

    • synchronized 는 아래 2가지 방법으로 모두 적용하였습니다.

      • 메서드 자체에 적용
      • 객체(Object)에 적용
        public synchronized void orderSync(String productName, int amount) {
            order(productName, amount);
        }
        public void orderSyncObject(String productName, int amount) {
            synchronized (this) {
                order(productName, amount);
           }
       }
  2. ReentrantLock 사용

private final Lock lock = new ReentrantLock();

public void orderReentLock(String productName, int amount) {
    lock.lock();                        // 락 획득
    try  {
        order(productName, amount);     // 주문 시도
    } finally {
        lock.unlock();                  // 락 해제
    }
}
  1. ConcurrentHashMap 사용
private final Map<String, Integer> productDatabase = new ConcurrentHashMap<>();
private final Map<String, OrderInfo> latestOrderDatabase = new ConcurrentHashMap<>();

// 주문 처리 메서드
public void order(String productName, int amount) {
    productDatabase.compute(productName, (key, currentStock) -> {
        // 현재 재고가 존재하지 않거나, 남은 재고가 주문하려는 `amount`보다 적다면 현재 재고를 반환한다.
        if (currentStock == null || currentStock < amount) {
            return currentStock;
        }
        System.out.println("Current Thread : " + Thread.currentThread().getName() +
                " - CurrentStock : " + currentStock + " - Order : " + amount);
        latestOrderDatabase.put(productName, new OrderInfo(productName, amount, System.currentTimeMillis()));
        return currentStock - amount;
    });
}
  1. ThreadLocal 을 이용한 제어
private final HashMap<String, AtomicInteger> productDatabase = new HashMap<>();
private final HashMap<String, OrderInfo> latestOrderDatabase = new HashMap<>();
private final ThreadLocal<OrderInfo> threadLocalOrderInfo = ThreadLocal.withInitial(() -> null);

// 주문 처리 메서드
public void order(String productName, int amount) {
    // 주문 정보 설정
    threadLocalOrderInfo.set(new OrderInfo(productName, amount, System.currentTimeMillis()));

    AtomicInteger currentStock = productDatabase.get(productName);
    if (currentStock == null || currentStock.get() < amount) {
        return;
    }

    // 재고 감소
    boolean success = currentStock.compareAndSet(currentStock.get(), currentStock.get() - amount);
    if (success) {
        String value = "Current Thread : %s - CurrentStock : %d - Order : %d".formatted(
                Thread.currentThread().getName(), currentStock.get(), amount);
        System.out.println(value);

        // 최신 주문 정보 업데이트
        latestOrderDatabase.put(productName, threadLocalOrderInfo.get());
    } else {
        System.out.println("Order failed due to concurrent update for " + productName);
    }
}

모두 테스트를 생성하여 처리 하였으며 각 테스트의 통과도 확인하였습니다.


발생했던 문제와 해결 과정을 남겨 주세요.

  • ConcurrentHashMap과 ThreadLocal을 사용하여 동시성을 제어하는 방식은 처음 구성해보는거라 많이 헤맨거 같습니다.
  • ConcurrentHashMap은 Map 전체에 락을 거는게 아니라 특정 키에 대해서만 synchronized락을 거는 방식으로 동작을 하기 때문에 멀티스래드 환경에서 동시성을 제어하기에 적절하다는 판단으로 적용했습니다.
  • ThreadLocal의 경우 각 Thread 마다 독립적인 데이터를 저장하여 공유자원에 접근시 AtomicInteger를 활용하여 현재 stock이 Map을 검색하여 나온 stock과 동일하면 주문 수량을 현재 재고에서 차감하는 방식으로 만들었습니다.

이번 주차에서 고민되었던 지점이나, 어려웠던 점을 알려 주세요.

과제를 해결하며 특히 어려웠던 점이나 고민되었던 지점이 있다면 남겨주세요.

  • 단순히 동시성 제어를 위해 synchronized 키워드나 ReentrantLock 을 사용하는 등의 이론적인 방식은 알고 있으나 어떠한 원리로 동작을 하는지 그리고 동시성을 제어하기 위해, 어떠한 상황에서 어떤 동시성 제어 방법을 선택해야 하는지에 대해 명확히 구분하고 있지 못한거 같아 고민이 됩니다. (현업에서 동시성을 고려한 코드 구성을 많이 생각해보지 않아서 그런거 같습니다.)

리뷰 포인트

  • ThreadLocal을 적용한 코드를 확인해주시면 감사하겠습니다.

기타 질문

  • Redis를 이용하여 캐시 작업을 할 때, 조회가 자주 일어나는 작업을 캐시한다고 알고 있습니다. 혹시 서비스 쪽에서 업무를 하신다면 현업에서는 구체적으로 어떤 데이터의 캐쉬 작업을 하시나요? (저는 현업에서 카테고리 및 검색을 위한 코드성 데이터를 인메모리 캐시로 캐시 작업을 하여 조회를 고려하려고 했습니다.)
  • 프로젝트를 구성할 때, 데이터 중심이 아닌 도메인 중심으로 구성을 해 나가는 방식이 아직 적용이 잘 되지 않습니다. 데이터를 생각하는 습관이 되서 그런거 같은데 어떤식으로 접근해야 도메인 중심으로 프로젝트를 생성할 수 있을 까요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant