호출된 함수의 결과에는 관심이 없지만, 제어권을 가지지 못하기 때문에 호출된 함수의 작업이 완료될 때까지 대기하게 된다.
이점이 없는 방식이기 때문에 잘 구현하지 않는다. 주로 Async-NonBlocking 구조를 구현하려다가 제어권을 잘 반환하지 못해서 Async-Blocking 으로 동작하는 경우가 종종 있다.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
@FunctionalInterface
public interface Supplier<T> {
T get();
}
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public static void main(String[] args) {
var consumer = getConsumer();
consumer.accept(1);
var consumerAsLambda = getConsumerAsLambda();
consumerAsLambda.accept(1);
handleConsumer(consumer);
}
// 익명 클래스로 구현
public static Consumer<Integer> getConsumer() {
Consumer<Integer> returnValue = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
log.info("value in interface: {}", integer);
}
};
return returnValue;
}
// 익명클래스 대신 람다 식으로 구현
public static Consumer<Integer> getConsumerAsLambda() {
return integer -> log.info("value in lambda: {}", integer);
}
public static void handleConsumer(Consumer<Integer> consumer) {
log.info("handleConsumer");
consumer.accept(1);
}
output
29:47 [main] - value in interface: 1
29:47 [main] - value in lambda: 1
29:47 [main] - handleConsumer
29:47 [main] - value in interface: 1
@Slf4j
public class A {
public static void main(String[] args) {
log.info("Start main");
var result = getResult();
var nextValue = result + 1;
assert nextValue == 1;
log.info("Finish main");
}
public static int getResult() {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
var result = 0;
try {
return result;
} finally {
log.info("Finish getResult");
}
}
}
output
22:47:30.075 [main] INFO -- Start main
22:47:30.077 [main] INFO -- Start getResult
22:47:31.081 [main] INFO -- Finish getResult
22:47:31.082 [main] INFO -- Finish main
main
은 getResult
의 결과 값에 관심이 있다.main
은 결과를 이용해서 다음 코드를 실행한다.public class B {
public static void main(String[] args) {
log.info("Start main");
getResult(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
var nextValue = integer + 1;
assert nextValue == 1;
}
});
log.info("Finish main");
}
public static void getResult(Consumer<Integer> cb) {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
var result = 0;
cb.accept(result);
log.info("Finish getResult");
}
}
output
22:47:43.871 [main] INFO -- Start main
22:47:43.873 [main] INFO -- Start getResult
22:47:44.878 [main] INFO -- Finish getResult
22:47:44.879 [main] INFO —- Finish main
main
은 getResult
의 결과에 관심이 없다getResult
는 결과를 이용해서 함수형 인터페이스
를 실행한다caller
는 결과를 이용해서 action을 수행한다caller
는 callee
의 결과에 관심이 있다caller
는 callee
의 결과에 관심이 없다callee
는 결과를 이용해서 callback
을 수행한다main
은 getResult
가 완료될 때까지 대기한다main
는 getResult
가 결과를 돌려주기 전까지 아무것도 할 수 없다main
은 getResult
가 결과를 구하고 callback
을 실행하기 전까지 아무것도 할 수 없다main
은 getResult
가 완료될 때까지 대기한다callee
를 호출한 후, callee가 완료되기 전까지 caller
가 아무것도 할 수 없다callee
가 가지고 있다public static void main(String[] args)
throws InterruptedException, ExecutionException {
log.info("Start main");
var count = 1;
Future<Integer> result = getResult();
while (!result.isDone()) {
log.info("Waiting for result, count: {}", count++);
Thread.sleep(100);
}
var nextValue = result.get() + 1;
assert nextValue == 1;
log.info("Finish main");
}
public static Future<Integer> getResult() {
var executor = Executors.newSingleThreadExecutor();
try {
return executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
var result = 0;
try {
return result;
} finally {
log.info("Finish getResult");
}
}
});
} finally {
executor.shutdown();
}
}
output
47:06 [main] - Start main
47:06 [pool-2-thread-1] - Start getResult
47:06 [main] - Waiting for result, count: 1
47:06 [main] - Waiting for result, count: 2
47:07 [main] - Waiting for result, count: 3
47:07 [main] - Waiting for result, count: 4
47:07 [main] - Waiting for result, count: 5
47:07 [main] - Waiting for result, count: 6
47:07 [main] - Waiting for result, count: 7
47:07 [main] - Waiting for result, count: 8
47:07 [main] - Waiting for result, count: 9
47:07 [main] - Waiting for result, count: 10
47:07 [pool-2-thread-1] - Finish getResult
47:07 [main] - Finish main
getResult
를 호출한 후, getResult
가 완료되지 않더라도 main
은 본인의 일을 할 수 있다.caller
는 callee
가 완료될 때 까지 대기한다.callee
가 가지고 있다caller
와 다른 별도의 thread가 필요하지 않다 (혹은 thread를 추가로 쓸 수도 있다)caller
는 callee
를 기다리지 않고 본인의 일을 한다.caller
가 가지고 있다caller
와 다른 별도의 thread가 필요하다ublic static void main(String[] args) {
log.info("Start main");
getResult(new Consumer<Integer>() {
@Override
public void accept(Integer
integer) {
var nextValue = integer + 1;
assert nextValue == 1;
}
});
log.info("Finish main");
}
public static void getResult(Consumer<Integer> callback) {
var executor = Executors.newSingleThreadExecutor();
try {
executor.submit(new Runnable() {
@Override
public void run() {
log.info("Start getResult");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
var result = 0;
try {
callback.accept(result);
} finally {
log.info("Finish getResult");
}
}
});
} finally {
executor.shutdown();
}
}
[main] INFO -- Start main
[pool-1-thread-1] INFO -- Start getResult
[main] INFO -- Finish main
[pool-1-thread-1] INFO -- Finish getResult
main
은 getResult
의 결과에 관심이 없다getResult
를 호출한 후, getResult
가 완료되지 않더라도 main은
본인의 일을 할 수 있다blocking
은 thread
가 오랜 시간 일을 하거나 대기하는 경우 발생CPU-bound blocking
: 오랜 시간 일을 한다IO-bound blocking
: 오랜 시간 대기한다IO-bound non-blocking
가능https://www.youtube.com/watch?v=oEIoqGd-Sns&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC
Sprin gWebflu x완전정복:코루틴부터 리액티브 MSA 프로젝트까지 (FastCampus)
• 하나의 함수에서 여러 함수를 호출하기도 하고, 함수 호출은 중첩적으로 발생
• callee
는 caller
가 되고 다시 다른 callee
를 호출
• blocking
한 함수를 하나라도 호출한다면 caller
는 blocking
이 된다
• 함수가 non-blocking
하려면 모든 함수가 non-blocking이어야 한다
• 따라서 I/O bound blocking
또한 발생하면 안된다