스프링을 공부하기 전에 항상 나오는 말이 있다.
스프링은 동기, node 서버는 비동기라는 말을 굉장히 많이 들었던 것 같다.
처음에 공부할 때는 외우고만 있다가 해당 내용을 운영체제에서 공부하고 그때서야 이해할 수 있게 되었다.
이번에도 공부하기 전에 동기와 비동기에 대해서 정리하고 가고자 한다.
Caller와 Callee
프로그래밍에서 굉장히 많이 봤던 용어일 것이다.
caller는 말 그대로 다른 함수를 호출하는 함수이고, callee는 그때에 호출당하는 함수이다.
함수형 인터페이스
1개의 추상 메서드를 가지고 있는 인터페이스 함수를 1급 객체로 사용하는 것을 말한다.
자바에서는 람다 표현식을 이 함수형 인터페이스에서만 사용 가능하다.
package org.example;
@FunctionalInterface
public interface Function<T> {
void accept(T t);
}
해당 함수형 인터페이스를 사용해보도록 하자.
package org.example;
public class Main {
public static void main(String[] args) {
var function = getFunction();
function.accept(1);
var functionAsLambda = getFunctionAsLambda();
functionAsLambda.accept(1);
functionHandler(function);
}
public static Function<Integer> getFunction(){
Function<Integer> returnValue = new Function<Integer>() {
@Override
public void accept(Integer integer) {
myLog("value in interface: " + integer);
}
};
return returnValue;
}
public static Function<Integer> getFunctionAsLambda(){
return integer -> myLog("value in lambda: " + integer);
}
public static void functionHandler(Function<Integer> function){
myLog("functionHandler");
function.accept(1);
}
public static void myLog(String string){
System.out.println(Thread.currentThread() + " -> " +string);
}
}
이 코드를 실행해보면
이때 모두 메인 스레드에서 실행이 되는 것을 볼 수 있다.
함수 호출 관점
A 프로그램
package org.example;
public class programA {
public static void myLog(String string){
System.out.println(Thread.currentThread() + " -> " +string);
}
public static void main(String[] args) {
myLog("start main");
var value = getValue();
var nextValue = value + 1;
myLog(String.valueOf(value == nextValue));
myLog("Finish main");
}
public static int getValue(){
myLog("start getValue");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
throw new RuntimeException(e);
}
var value = 0;
try{
return value;
}finally {
myLog("Finish getResult");
}
}
}
이 모델의 실행을 그림으로 표현하면 다음과 같다.
main 함수는 getValue의 실행이 끝날 때까지 기다린 후 해당 데이터를 가지고 작업을 한다.
이것과는 다른 B 프로그램이다.
package org.example;
public class programB {
public static void myLog(String string){
System.out.println(Thread.currentThread() + " -> " +string);
}
public static void main(String[] args) {
myLog("start main");
getValue(integer -> {
var nextValue = integer + 1;
myLog(String.valueOf(nextValue == 1));
});
myLog("Finish main");
}
public static void getValue(Function<Integer> function){
myLog("Start getValue");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
throw new RuntimeException(e);
}
var value = 0;
function.accept(value);
myLog("Finish getValue");
}
}
해당 프로그램은 main에서 직접 실행하는 것이 아니라 getValue에서 실행하도록 한다.
이 프로그램을 그림으로 나타내면 다음과 같다.
두 프로그램의 차이점은
A 프로그램은 다음 코드를 위해 callee의 반환값이 필요하다.
B 프로그램은 다음 코드를 위해 callee의 반환값이 필요하지 않고 callee가 결과를 이용해서 callback을 수행한다.
이러면 A 프로그램을 동기, B 프로그램을 비동기라고 생각을 하면 된다.
'백엔드 > 리액티브 프로그래밍' 카테고리의 다른 글
CompletableFuture를 사용한 성능튜닝 (0) | 2024.03.06 |
---|---|
CompletableFuture 인터페이스 (1) | 2024.03.06 |
CompletionStage 인터페이스 (1) | 2024.03.05 |
Future 인터페이스 (1) | 2024.01.09 |
Blocking, Non-blocking이란 무엇일까? (0) | 2024.01.08 |