[Java 동시성] 동기, 비동기와 블로킹, 논블로킹의 개념과 사례

동기/비동기는 작업 순서의 관점에서, 블로킹/논블로킹은 제어권의 관점에서 바라보는 프로그래밍 실행 방식입니다.



Sync/Async & Blocking/Non-blocking

  • 동기: 작업 순서가 보장됨
  • 비동기: 작업 순서가 보장되지 않음
  • 블로킹: 제어권을 넘기지 않음
  • 논블로킹: 제어권을 넘김

Image

Image



실제 사례별 분석과 코드

실생활 사례를 생각해보면?

  1. Sync & Blocking
    • 은행 창구 상담번호표의 순서대로 상담(동기), 상담 중인 고객의 업무가 끝날 때까지 다음 고객은 대기(블로킹)
  2. Async & Non-blocking
    • 카페 주문 처리주문 순서와 음료 완성 순서 무관(비동기), 바리스타가 음료 제조 중에도 다른 주문 접수 가능(논블로킹)
  3. Sync & Non-blocking
    • 식당 대기 순서대로 입장(동기), 대기 중 다른 활동 가능(논블로킹)
  4. Async & Blocking
    • 중고거래 상대 선택 순서 자유(비동기), 한 거래 완료 전 다른 거래 불가(블로킹)



코드로 이해해보자

100퍼센트 정확한 예시는 아니지만, 이런 느낌으로 흘러간다는 것을 보여주기 위한 코드이다.

  1. Sync & Blocking

    // 동기적으로 파일 읽기
    FileInputStream fis = new FileInputStream("file.txt");
    byte[] data = new byte[100];
    fis.read(data);
       
    FileInputStream fis2 = new FileInputStream("file2.txt");
    byte[] data2 = new byte[100];
    fis2.read(data2);
    
    • 파일을 읽는 동안 스레드가 차단되며(Blocking)
    • 작업이 순차적으로 실행됨(Sync)
  2. Async & Non-blocking

    // CompletableFuture: 비동기 지원
    CompletableFuture.supplyAsync(() -> {
        return heavyTask();
    }).thenAccept(result -> {
        System.out.println("처리 완료: " + result);
    });
    System.out.println("done");
    
    • 작업이 비동기적으로 실행(Async)
    • 다른 작업 실행 가능(Non-Blocking)
      • 즉, “처리 완료” 보다 done”이 먼저 출력 될 수 있다.
      • 1번째 예제인 파일 읽기를 이곳에 적용하면?
        파일 따로 읽으면서 메인 코드는 계속 다른 일을 수행할 수 있는 것이다.
  3. Sync & Non-blocking

    // Java NIO Selector 예제
    Selector selector = Selector.open();
    ServerSocketChannel serverSocket = ServerSocketChannel.open();
    serverSocket.configureBlocking(false);  // non-blocking 모드 설정
    serverSocket.register(selector, SelectionKey.OP_ACCEPT);
       
    while (true) {
        selector.select();  // 이벤트가 발생할 때까지 대기
        Set<SelectionKey> selectedKeys = selector.selectedKeys();
           
        for (SelectionKey key : selectedKeys) {
            if (key.isAcceptable()) {
                // 새로운 연결 처리
            } else if (key.isReadable()) {
                // 데이터 읽기 처리
            }
        }
        selectedKeys.clear();
    }
       
    
    • Sync: selector.select()를 통해 이벤트를 순차적으로 처리
    • Non-Blocking: 채널들이 non-blocking 모드로 설정되어 있어 I/O 작업이 즉시 반환
    • 보충 설명
      • 위 예제에선 Java NIO로 양방향 TCP 통신하는 실시간 채팅을 구현할 수 있는데,
        요청 순서대로 채팅이 올라오고(Sync),
        채팅이 올라오면서 언제든지 채팅을 입력할 수도 있다.(Non-blocking)
      • 번외) 백그라운드에서 돌아가는 요청 작업을 스레드1개, 메인 코드가 수행되는 메인 스레드 1개가 있다고 생각해보자.
        while문을 통해 스레드가 모두 처리되었는지 끊임없이 확인하고, 처리가 완료되면 다음 메인 작업을 수행한다.
      • 번외) JS의 async/await은 비동기 논블로킹 방식을 동기 논블로킹 방식으로 바꿔주는 기법이니까 JS에서 비동기를 우린 동기처럼 사용가능 한 것이었다.
        그러나, 내부적으로 비동기 논블로킹 방식으로 동작한다는 점 유의.
  4. Async & Blocking

    // Future: 비동기 지원
    Future<ResultSet> future = executor.submit(() -> {
        return statement.executeQuery("SELECT * FROM users");
    });
    ResultSet result = future.get();
    
    • future.get()이 결과를 기다리며 스레드를 차단(Blocking)
      • Java의 Future가 비동기라 해도, MySQL 드라이버가 블로킹 방식이다.
      • MySQL의 결과값을 필요로 하다보니 기다리게 되는데, 결국 Sync Blocking 작업 수행과 차이가 없게 된다.
      • 이해하기 어렵긴 한데, 어차피 실무에서 볼 일은 거의 없다.
    • 작업은 비동기적으로 실행(Async)

각 방식은 상황에 따라 적절히 선택되어야 하며, 시스템의 요구사항과 특성을 고려해야 합니다.

댓글남기기