동기
- 코드 작성 순에 따라 작업 실행
- 이전 작업 완료 후 다음 작업 시작
- 작업이 빠르게 수행되는 경우, 순서와 작업간 의존성이 중요한 경우 등
비동기
- 전의 작업이 완료되기를 기다리지 않고 바로 시작
- 스레드 혹은 이벤트 핸들러에서 비동기적으로 실행 가능
- 백그라운드에서 동시에 여러작업 진행 가능
- 네트워크 호출, 긴 시간이 걸리는 작업 등
1. ExcutorService 인터페이스
- 여러 작업을 비동기적으로 처리하기 위해 제공되는 JAVA 라이브러리
(1) 생성
- newFixedThreadPool(int) : 인자 개수만큼 고정된 스레드풀을 생성
- newCachedThreadPool(): 필요할 때, 필요한 만큼 스레드풀을 생성
- newSingleThreadExecutor(): 스레드가 1개인 ExecutorService를 리턴. 싱글 스레드
ExecutorService executorService = Executors.newFixedThreadPool(4);
ExecutorService executorService = Executors.newSingleThreadExecutor();
(2) 작업 할당
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> {});
- execute()
- 반환 타입이 void로 작업의 결과나 상태를 알 수 없음
- invokeAny()
- 작업을 Collection에 넣어 인자로 전달하면 실행에 성공한 작업 중 하나의 반환값을 반환
- invokeAll()
- 작업을 Collection에 넣어 인자로 전달하면 모든 반환값을 List< Future <>>로 반환
2. @Async
- 해당 애너테이션이 붙은 메서드가 비동기적으로 실행
- 클래스 레벨에 @EnableAsync를 붙여 사용
- 간단한 방식으로 비동기 방식 사용 가능
Event
- 서비스 간의 의존성을 줄이고 확장성과 유지보수를 위해 사용
- 예를들어 회원 가입 자체를 하나의 이벤트로 설정해 회원 가입시 알림 메일 발송 등 부가적인 프로세스들을 Service 클래스에서 분리하여 처리
- 아래 예시는 트랜잭션 내부에 비동기적으로 실행되는 회원가입시 확인 메일 발송 기능 중 메일 발송에 실패하면 가입된 회원의 정보가 삭제되는 기능 구현
1. Event 클래스
@Getter
public class MemberEvent {
private Member member;
public MemberEvent(Member member) {
this.member = member;
}
}
2. EventPublisher
- 이벤트를 발행하는 역할
- ApplicationEventPublisher 인터페이스를 사용
- 스프링 컨텍스트가 해당 인터페이스를 구현한 빈을 내부적으로 등록
@Slf4j
@Transactional
@Service
public class MemberService {
private final MemberRepository memberRepository;
private final ApplicationEventPublisher eventPublisher;
public MemberService(MemberRepository memberRepository, ApplicationEventPublisher eventPublisher) {
this.memberRepository = memberRepository;
this.eventPublisher = eventPublisher;
}
public Member createMember(Member member) {
verifyExistsEmail(member.getEmail());
Member savedMember = memberRepository.save(member);
log.info("# Saved member");
eventPublisher.publishEvent(new MemberEvent(member));
return savedMember;
}
...
}
- publishEvent() 인자에 Object 타입을 넣어 이벤트 발행
3. @EventListener
- 이벤트 핸들러 메서드에 붙이며 이벤트가 발생하면 해당 이벤트와 매칭되는 이벤트 핸들러 메서드를 실행
- 발행된 이벤트의 타입에 따라 매칭
@Slf4j
@EnableAsync
@Component
public class MemberEventListener {
private MemberService memberService;
private EmailSender emailSender;
public MemberEventListener(MemberService memberService, EmailSender emailSender) {
this.memberService = memberService;
this.emailSender = emailSender;
}
@EventListener
@Async
public void sendEmail(Member member) throws Exception {
try {
emailSender.sendEmail("any email message");
} catch (Exception e) {
log.error("MailSendException happened: ", e);
memberService.deleteMember(member.getMemberId());
}
}
}