[Java]동기화? 비동기화? 그 차이가 뭘까?

두지·2023년 3월 27일
1
post-thumbnail
post-custom-banner

> 동기화(Synchronous)와 비동기화(Asynchronous)는 프로그래밍에서 두 가지 실행 모델을 나타내는 용어이다.

동기화는 여러 작업이 동시에 실행될 때 한 작업이 끝날 때까지 다른 작업이 시작되지 않는 것을 의미한다.

프로세스/스레드 관점

동기화(Synchronization)은 여러개의 스레드 또는 프로세스가 공유 자원을 접근할 때, 서로의 작업이 서로의 영향을 미치지 않도록 조절하는 것을 말한다. 동기화를 통해 공유 자원에 대한 접근을 제한함으로써, 스레드 또는 프로세스 간의 경쟁상황이 발생하지 않도록 하여 일관성 있는 결과를 보장할 수 있게된다. 대표적인 예시로는 뮤텍스, 세마포어가 있다.

예를들어, 동기화의 대표적인 예시는 은행 계좌의 입/출금 작업을 생각해 볼수가 있다. 만약 여러 스레드가 동시에 같은 계좌에 접근하여 입금/출금 작업을 수행한다면, 계좌 잔액이나 입출금이 서로 어것나는 문제가 발생할 수 있다. 이러한 문제를 해결하기 위해 lock이라는 장치를 하여 점유중인 스레드를 제외한 스레드의 접근을 막아 일관성 및 무결성을 보장한다.

비동기화(Asynchronization)은 여러 작업을 동시에 처리하면서, 작업이 완료되기 전에 다른 작업을 수행할 수 있는 것을 말한다. 즉, 비동기화는 작업을 순차적으로 처리하는 동기화와 달리, 작업을 동시에 처리할 수 있어 처리 속도를 향상 시킬 수 있다. 대표적인 예시로는 비동기식 I/O와 콜백 등이 있다.

예를들어, 비동기화의 예시로는 웹 서버의 요청 처리를 생각할 수 있다. 여러 클라이언트로부터 요청이 동시에 들어오더라도, 비동기식 I/O 방식을 사용하면 요청을 동시에 처리할 수 있으므로 처리 속도를 향상시킬 수 있다. 또한, 요청이 처리되면, 클라이언트에게 결과를 반환하는 콜백 함수를 사용하여 비동기화를 구현할 수 있다.

사실, 개념은 OS의 프로세스/스레드와 똑같다. 어떤 관점으로 보느냐 따라 차이인거같다.

즉, 한 작업이 시작된다면 그 작업이 끝날 때까지 기다리고 다른 작업이 시작되지 않는 것이다. 이것은 순차적인 실행 모델을 따르는 것이므로 예를들어서 함수 호출이나 루프 반목분 등이 동기적으로 실행이 된다. 동기화 방식은 간단하고 이해하기 쉽지만, 작업이 끝나기 전까지 다른 작업을 수행하지 못하는 단점이 있다. 그러므로 시간이 많이 소요될 수 있다.

비동기화는 동기화랑 다르게 한 작업이 시작되면 다른 작업이 바로 시작될 수 있다는 것을 의미한다. 동기화랑 다르게 작업이 끝날 때까지 기다리지 않고 다른 작업을 동시에 함으로 비동기화 방식은 병렬적인 실행 모델을 따른다. 예를들어서 콜백함수(callback)함수나 프라미스(promise) 객체를 사용하여 비동기적으로 실행된다. 비동기화 방식의 장점은 여러 작업을 동시에 처리할 수 있어 빠르고 효율적이나, 복잡하고 디버깅이 어렵다는 단점이 있다.

자바에서 동기화와 비동기화를 구현할 수 있다.
아래는 자바로 구현한 동기화와 비동기화이다.

public synchronized void doSomething() {
    // 메서드 내용
}

비동기화는 자바에서 멀티스레딩, Runnable, Callabce, 콜백(callback), 프라미스(promise), CompletableFuture 등의 방법을 사용하여 구현할 수 있다.

멀티스레딩은 자바에서 Thread 클래스를 사용하여 구현한다.
아래는 Thread 클래스를 사용하여 새로운 스레드를 생성하고 실행하는 클래스이다.

Thread thread = new Thread(new Runnable() {
    public void run() {
        // 스레드에서 수행할 작업
    }
});
thread.start();

Runnable를 사용하여 비동기화를 하는 예제를 보여주겠다.

public class Example implements Runnable {

    public static void main(String[] args) {
        Example ex = new Example();
        Thread t = new Thread(ex);
        t.start();
        System.out.println("Main thread is running.");
    }

    @Override
    public void run() {
        System.out.println("New thread is running.");
    }
}

이 예제에서는 Runnable 인터페이스를 구현하여 새로운 스레드를 생성한다. 'main()' 메소드에서는 Example 클래스 객체를 생성한 후, Thread 객체를 생성하여 Example 객체를 전달한다. 그리고 start() 메소드를 호출하여 새로운 스레드를 시작한다.

run() 메소드에서는 새로운 스레드에서 실행될 코드를 작성한다.
이 예제는 "New thread is running"이 출력되도록 코드를 작성하였다.

main() 메소드에서는 "Main thread is running"이 출력된후 종료된다. 새로운 스레드에서는 "New thread is running"이 출력된다.
겉으론 잘 모르겠지만 main() 메소드와 run() 메소드가 동시에 실행되어 비동기화가 구현된다.

그 증거로 저 실행 코드를 계속 실행하다보면
"New thread is running"와 "Main thread is running"가 서로 번갈아가면서 출력된다.

콜백(callback)은 자바에서 인터페이스를 사용하여 구현했다. 콜백은 다른 메서드가 호출될 때 이를 받아들이고 실행하는 메서드이다. 콜백은 비동기적으로 실행되며, 작업이 완료가 되는 순간 호출된다.

public interface Callback {
    void onResult(int result);
}

public void doSomethingAsync(Callback callback) {
    // 비동기적으로 실행될 작업
    int result = 42;
    callback.onResult(result);
}

프라미스(promise)는 자바 8부터 추가된 CompletableFuture 클래스를 사용하여 구현할 수 있다. 이 클래스는 작업이 완료될 떄까지 기다리지 않고 다른 작업을 수행할 수 있다.

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 비동기적으로 실행될 작업
    return "result";
});

future.thenAccept(result -> {
    // 작업이 완료되면 호출될 콜백 함수
    System.out.println(result);
});

여기서 잠깐! 콜백 함수가 뭐야?

콜백(callback)함수는 다른 함수에서 인자로 받은 함수를 나중에 호출하는 것을 의미한다. 쉽게말해 콜백 함수는 어떤 A라는 함수의 파라미터에 함수가 들어가있는 것이다. 비동기처리에 많이 사용된다.

자바에서는 Callback 인터페이스를 사용하여 콜백함수를 구현한다.
자바로 예제를 보여주겠다.

public interface Callback {
    void onComplete(String result);
}

이것은 Callback 인터페이스이다. onComplete 메소드 하나만있는 인터페이스를 정의하였다.
result 인자를 받아들이고 반환값이 없다.

이제 이 Callback 인터페이스를 구현하는 클래스를 작성하겠다.

public class MyCallback implements Callback {
    @Override
    public void onComplete(String result) {
        // 콜백 함수에서 수행할 작업
    }
}

MyCallback 클래스를 다른 클래스에서 인스턴스로 생성하고 콜백함수로 사용할 수 있다.

public class MyClass {
    public void doSomething(Callback callback) {
        // 비동기적으로 수행될 작업
        String result = "result";
        callback.onComplete(result);
    }
}

MyClass myClass = new MyClass();
myClass.doSomething(new MyCallback());

이 예제에서는 MyClass 클래서가 doSomething 메소드에서 Callback 인터페이스를 인자로 받아들인다. 이 메소드가 작업이 완료되면 콜백 함수인 onComplete 메소드를 호출한다. 이때 MyCallback 클래스의 onComplete 메소드가 호출되어 수행한다.

비동기 프로그래밍과 멀티 스레딩의 차이점은 무엇일까?

일반적으로 많은 사람들이 비동기와 멀티 스레딩이 같은 것줄 안다고 한다. 하지만 그렇지 않다
유추는 일반적으로 도움이 된다.

상황에 대한 예시를 들어보겠다. 만약 누군가가 식당에서 요리하고 있다.
그때 계란과 토스트의 주문이 들어왔다.

동기식 : 한 요리사가 후라이팬을 사용하여 계란을 요리한 다음 토스트를 요리한다.

비동기식 단일 스레드 : 한 요리사가 후라이팬에 계란 후라이를 올려놓고 타이머를 설정한다. 그리고 토스트기에 토스트를 넣고 타이머를 설정한다. 둘다 요리하는 동안은 요리할 때 사용했던 요리도구를 청소하고 도마를 씻는다. 그러다가 타이머가 먼저 울리는 계란을 꺼내거나 토스트를 꺼낸다.

비동기식 멀티 스레드 : 토스트 요리를 위해 한 명더 요리사를 고용한다.이제 자원을 공유할 때 주방에서 동선이 겹치면 오히려 걸리적 거리며 토스트기에 토스트 안 넣고 후라이팬에 토스트를 넣으면 계란 후라이를 하려는 요리사는 기다려야한다. 동선이 겹치지 않게 같은 자원을 동시에 사용하지 않게 요리사를 조정하는게 중요하다.

여기서 스레드는 작업자이고, 비동기는 작업자의 요리 방식이다. 한번에 쏙 이해가되나요?

이러한 예시를 보면 또 알수 있는게 단일 스레드환경에서는 데드락이 걸릴 수가 없다. 멀티 스레딩 방식에서 경쟁 상황이 발생하는 법이다.

참고 문헌 : http://daplus.net/c-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EA%B3%BC-%EB%A9%80%ED%8B%B0-%EC%8A%A4%EB%A0%88%EB%94%A9%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87/

profile
인생은 끝이 없는 도전의 연속입니다. 저는 끝 없이 함께 새로운 도전을 합니다.
post-custom-banner

0개의 댓글