[JAVA] Concurrent 프로그래밍

유시준·2022년 5월 22일
0

시작에 앞서

클린코드에서 동시성 파트를 읽었는데 내용 자체가 어렵고 클린코드에서 Concurrent패키지와 Executors를 활용하라고 했는데 뭔지 몰라서 그런게 있구나 하고 넘어갔다. 근데 갑자기 기선님 강의 Concurrent를 들을 차례가 나와서 집중해서 들었다.

Concurrent 소프트웨어

  • 동시에 여러 작업을 할 수 있는 소프트웨어
  • 웹 브라우저로 유튜브를 보면서 키보드로 문서에 타이핑을 하는 것
  • 녹화를 하면서 인텔리제이로 코딩을 하고 워드에 적어둔 문서를 보거나 수정할 수 있다.

자바에서는 멀티 프로세싱과 멀티 쓰레드 Concurrent 프로그래밍을 지원한다.

자바 멀티 스레드 프로그래밍

  • Thread
public class Main {
    public static void main(String[] args) throws IOException {
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("Hello");
        //Hello와 1의 출력순서를 보장할 수 없다.
    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("1");
    }
}

위와 같이 Thread클래스를 상속받고 run메서드를 재정의 하여 Thread를 실행시키는 방법이 있다.

  • Runnable
public class Main{
    public static void main(String[] args) throws IOException {
        Thread thread = new Thread(()->{
            System.out.println("1");
        });
        thread.start();
        System.out.println("hello");
        //Hello와 1의 출력순서를 보장할 수 없다.
    }
}

Runnable을 Thread의 파라미터로 넘겨주어 내부 익명함수를 람다를 통해 run을 재정의 하여 사용할 수 있다.

  • 주요 기능
    • sleep : 다른 스레드가 처리할 수 있도록 기회를 준다 하지만 lock을 놓지는 않는다.
    • interupt : 다른 스레드를 깨워 InterruptedException을 발생시킴
    • join : 다른 스레드가 끝날 때까지 기다린다.

개발자가 Thread의 메서드를 통해 수십,수백개의 멀티 스레드를 저수준에서 직접 관리하기는 사실상 불가능이고 그렇게 하면 안됨

Executors

고수준 Concurrency 프로그래밍

  • 스레드를 만들고 관리하는 작업을 어플리케이션에서 분리
  • 그런 기능을 Executors에 위임

Executors가 하는일

  • 스레드 생성
  • 스레드 관리
  • 작업 처리 및 실행

주요 인터페이스

  • Executor : execute(Runnable)
  • ExecutorService : Executor를 상속받은 인터페이스
  • ScheduledExecutorService : ExecutorService를 상속 받는 인터페이스로 특정 시간 이후에 또는 주기적으로 작업을 실행할 수 있다.

ExecutorService의 내부 동작과정
(글씨체 ㅈㅅ)

Callable과 Future

Callable

  • Runnable과 유사하지만 작업의 결과를 받을 수 있다.
  • Runnable의 run은 void이기 때문에 return을 받을 수 없다.

Future

  • 비동기적인 작업의 현재 상태를 조회하거나 결과를 가져올 수 있다.

  • get() : 결과를 가져오기

    • 블록킹 콜이다.
    • 최대한으로 기다릴 시간을 설정할 수 있다.
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Callable<String> hello = ()->{
          Thread.sleep(2000L);
          return "Hello";
        };
        Future<String> submit = executorService.submit(hello);
        System.out.println("start");
        submit.get();//스레드가 종료될때 까지 계속 대기 == 블록킹 콜
        System.out.println("end");
        bw.flush();
    }
  • isDone() : 작업 상태 확인하기

    • 완료 했으면 true 아니면 false
  • cancel() : 작업 취소하기

    • 취소 했으면 true 못했으면 false를 return
  • invokeAll() : 여러 작업 동시에 실행하기

    • 동시에 실행한 작업 중에 제일 오래 걸리는 작업 만큼 시간이 걸린다.
  • invokeAny() : 여러 작업중에 하나라도 먼저 응답이 오면 끝내기

    • 동시에 실행한 작업 중에 제일 짧게 걸리는 작업 만큼 시간이 걸린다.
    • 블록킹 콜이다.

Completable Future

자바에서 비동기 프로그래밍을 가능하게 하는 인터페이스

Future의 단점

  • Future를 사용해도 어느정도 가능했지만 제약사항이 많다.
    • Future를 외부에서 완료 시킬 수 없다.
    • 블로킹 코드(get())를 사용하지 않고서는 작업이 끝났을 때 콜백을 실행할 수 없다.
    • 여러 Future를 조합할 수 없다.
    • 예외 처리용 API를 제공하지 않는다.

CompletableFuture

  • runAsync
    리턴값이 없어서 리턴 타입을 Void로 준다.
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            System.out.println("sijun");
        });
        future.get();

    }
  • supplyAsync
    리턴값을 줄 수 있다.
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "sijun";
        });
        System.out.println(future.get());
    }
  • thenApply(Function)
    콜백함수를 설정할 수도 있음
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "sijun";
        }).thenApply((s)->{
            return s.toUpperCase(Locale.ROOT);
        });
        System.out.println(future.get());
    }
  • thenCompose
    두 작업을 이어서 실행하도록 조합함
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            return "Sijun";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            return "Love";
        });
        System.out.println(future1.thenCompose(Main::getLove).get());
    }
    private static CompletableFuture<String> getLove(String msg){
        return CompletableFuture.supplyAsync(() -> {
            return msg+"Love";
        });
    }
  • thenCombine()
    두 작업을 독립적으로 실행하고 둘 다 종료 했을 때 콜백 실행

  • allOf()
    여러 작업을 모두 실행하고 모든 작업 결과에 콜백 실행

  • anyOf()
    여러 작업 중에 가장 빨리 끝난 하나의 결과에 콜백 실행

profile
금꽁치's Blog

0개의 댓글