동기, 비동기, 싱글 스레드, 멀티 스레드

Seung jun Cha·2023년 6월 1일
0

1. 개념

  • 동기와 비동기는 작업순서, 스레드는 작업공간이라고 이해하면 될 것 같다.
    • 동기 : 일이 들어온 순서대로 처리
    • 비동기 : 일이 들어온 순서에 상관없이 처리
    • 싱글 스레드 : 일을 처리하는 공간이 1개
    • 멀티 스레드 : 일을 처리하는 공간이 2개 이상

2. 싱글 스레드 - 동기

  • 하나의 공간에서 순서대로 일을 처리한다. A,B,C는 각각 다른 작업을 의미한다.

3. 싱글 스레드 - 비동기

  • 하나의 공간에서 순서에 관계없이 일을 처리한다.

4. 멀티 스레드 - 동기

  • 여러 개의 공간에서 순서대로 일을 처리한다. 동기방식이라면 스레드가 1개의 일을 처리하는동안 처리해야하는 다른 작업이 있더라도 처리중인 작업이 완료되기 전까지 다른 스레드를 활용할 수 없으므로 비효율적이다.

5. 멀티 스레드 - 비동기

  • 여러 개의 공간에서 순서에 상관없이 일을 처리하므로, 처리해야하는 작업이 많다면 멀티스레드-비동기 방식은 효율적이다.

6. @Async 사용 방법

단순히 비동기처리 기능만 구현하려면 비동기로 작업을 수행하려는 메서드 위에 @Async만 붙여주면 된다. 하지만 이 방법은 스레드를 생성만 하고 따로 관리는 하지 않기 때문에 위험한 방법이고, 프록시 객체 관련해서 클래스 내부의 @Async는 적용되는 않는 상황이 발생할 수 있으므로 주의해야 한다.

@EnableAsync 애노테이션은 스프링 컨텍스트에 비동기 처리를 위한 관련 빈들을 등록한다.
@SpringBootApplication이 사용된 클래스에 @EnableAsync를 사용하게 되면, @SpringBootApplication에 포함된 @Configuration 애노테이션과 충돌하게 되어, 중복된 설정이 발생할 수 있습니다.
그러므로 Async 설정 클래스를 만들어서 @EnableAsync를 붙여준다.

@Configuration
@EnableAsync   // 비동기 어노테이션을 사용할 수 있게 함
public class AsyncConfig {

     @Bean(name = "defaultTaskExecutor", destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor defaultTaskExecutor(){
        ThreadPoolTaskExecutor defaultTaskExecutor = new ThreadPoolTaskExecutor();
        defaultTaskExecutor.setCorePoolSize(10);
        defaultTaskExecutor.setMaxPoolSize(20);
        defaultTaskExecutor.setKeepAliveSeconds(3);
        return defaultTaskExecutor;
    }

    @Bean(name = "messagingTaskExecutor",  destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor messagingTaskExecutor(){
        ThreadPoolTaskExecutor messagingTaskExecutor = new ThreadPoolTaskExecutor();
        messagingTaskExecutor.setCorePoolSize(10);
        messagingTaskExecutor.setMaxPoolSize(20);
        messagingTaskExecutor.setKeepAliveSeconds(3);
        return messagingTaskExecutor;
    }
}

ThreadPoolTaskExecutor 는 스레드를 관리한다. 위의 코드는 스레드풀을 2개 만들었고, 아래가 기본적인 과정이다.
1 .corePoolSize 만큼 스레드를 생성한다.
2. workQueue 에 task를 담는다
3. workQueue 가 다 차면 maxiumPoolSize 만큼 새로운 스레드 생성한다.
4. keepAliveSeconds에 설정된 시간만큼 스레드를 사용하지 않으면 회수한다.

    private final EmailService emailService;

    /**
     * 2번과 2번은 비동기 처리가 되지 않는다
     * 비동기 처리를 하기위해서는 프록시 객체가 필요한데  2번은 등록된 빈이 아니고 3번은 프록시객체가 아니다
     */
    public void asyncCall_1(){   
        System.out.println("[asyncCall_1] : " + Thread.currentThread().getName());  
        emailService.sendEmail();    
        emailService.sendMailWithCustomThreadPool(); 
    }

    public void asyncCall_2(){  // 객체를 생성해서 인스턴스 내의 메서드를 사용하는 경우
        System.out.println("[asyncCall_2] : " + Thread.currentThread().getName());  // 이 메서드를 처리하는 스레드의 이름을 가지고 온다
        EmailService emailService1 = new EmailService();
        emailService1.sendEmail(); 
        emailService1.sendMailWithCustomThreadPool();  
        // 하나의 스레드가 처리
    }

    public void asyncCall_3(){  
    // @Async가 선언된 클래스 내의 메서드를 사용하는 경우
        System.out.println("[asyncCall_3] : " + Thread.currentThread().getName());  // 이 메서드를 처리하는 스레드의 이름을 가지고 온다
    }

    @Async
    public void sendMail(){
        System.out.println("[sendEmail] : "+ Thread.currentThread().getName());
    }

1번 메서드는 빈으로 등록된 비동기 메서드를 사용하는 경우로 문제없이 비동기가 작동한다. 즉, sendEmail메서드sendMailWithCustomThreadPool메서드는 다른 스레드로 처리가 된다.

-참고
동기? 비동기? 쓰레드? 멀티 쓰레드?
https://steady-coding.tistory.com/611

0개의 댓글