

호출된 함수의 결과에는 관심이 없지만, 제어권을 가지지 못하기 때문에 호출된 함수의 작업이 완료될 때까지 대기하게 된다.
이점이 없는 방식이기 때문에 잘 구현하지 않는다. 주로 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 또한 발생하면 안된다