많이 공부했었던 내용이니 용어정리만 하고 넘어간다.
여러 스레드가 동시에 공유 자원에 접근할 때, 접근 순서에 따라 결과가 달라지는 상황을 경쟁 상태라고 한다.
임계 영역은 동시에 둘 이상의 스레드나 프로세스가 접근하면 안 되는 공유 자원에 접근하는 코드 영역을 말한다.
뮤텍스는 mutual exclusion의 줄임말인데 뮤텍스를 다른 말로 잠금이라고도 한다. 프로그래밍 언어에 따라 뮤텍스를 사용하기도 한다. 자바 언어는 이름이 Lock인 타입을 사용한다.
세마포어는 동시에 실행할 수 있는 스레드 수를 제한한다. 자원에 대한 접근을 일정 수준으로 제한하고 싶을 때 세마포어를 사용할 수 있다.
CAS는 Compare And Swap의 약자로 이름 그대로 비교 후에 교체하는 연산을 말한다. CAS 연산은 원자적이다.
비관적 잠금을 사용하면 동일한 레코드에 대해 한 번에 하나의 트랜잭션만 접근할 수 있도록 제어할 수 있다.
값을 비교해서 수정하는 방식이다. 쿼리 실행 자체를 막지 않으면서도 데이터가 잘못 변경되는 것을 막을 수 있다.
update SUBJECT set joinCount = joinCount + 1 where id = ?
DB는 동일 데이터에 대한 원자적 연산이 실행될 경우 이를 순차적으로 실행한다.
증분 쿼리는 DB에 따라 원자적 연산이 아닐 수도 있기 때문에 사용하는 DB가 원자적으로 처리하는지 반드시 검증해야 한다.
교착 상태는 2개 이상의 스레드가 서로가 획득한 잠금을 대기하면서 무한히 기다리는 상황을 말한다.
계속 상태가 변경되지만 교착 상태가 지속되는 것을 뜻한다. (ex. 철학자의 만찬 문제)
우선 순위를 두는 방식으로 라이브락을 해소할 수 있다.
우선순위가 높은 작업이 많아 우선 순위가 낮은 작업이 실행이 안 될 수 있다. 이렇게 프로세스나 스레드가 자원을 할당받지 못해 실행되지 못하는 상태를 기아 상태라고 부른다.
동시성 문제가 발생하는 주된 이유는 여러 스레드가 동시에 동일 자원에 접근하기 때문이다. 이를 방지하기 위해 잠금과 같은 수단을 사용하지만 잘못 사용하면 교착상태 같은 상황이 발생할 수 있다.
여러 스레드가 접근하려는 동일 자원에 대해 하나의 스레드만 접근 가능하게 하면 동시성 문제가 발생하지 않게 된다. 작업 요청 스레드 여러개가 단일 접근 스레드에게 작업 큐에 담아 요청을 보내면 접근 스레드가 작업을 처리하여 반환한다. 병목 상태가 발생하는 것 같이 느껴질 수도 있지만, 생각해보면 여러 스레드가 하나의 자원을 획득하기 위해 기다리나 접근 스레드가 작업 결과를 반환해주길 기다리나 그게 그거다.
서버 API를 만들때 개발자가 직접 네트워크 프로그램을 작성하지는 않지만, 서버는 네트워크 통신을 기반으로 동작한다. 네트워크를 통해서 데이터 읽기/쓰기(데이터 입출력, I/O)를 한다.
데이터 입출력이 완료될 때까지 스레드는 아무 작업도 하지 않고 입출력이 끝나기를 기다린다. 스레드가 대기하는 시간을 소요한다는 것은, 그 스레드를 실행하는 CPU로 아무것도 하지 않는 시간이 생긴다는 의미이다. 요청당 스레드 방식으로 구현한 서버가 이에 해당한다. (ex. Tomcat)
메모리를 늘려 스레드를 많이 만들어서 여러 요청을 동시에 수행할 수 있도록 하면 문제가 해결되는가? 아니다. 그럼 또 다른 문제가 생기는데 바로 컨텍스트 스위칭이다.
운영체제는 여러 스레드를 번갈아 가면서 CPU에 할당한다. 한 스레드를 짧은 시간 동안 실행하고 다음 스레드를 짧은 시간 실행하는 식이다. CPU가 스레드를 전환할 때 현재 실행 중인 스레드의 상태를 기록하고 다음 스레드의 상태를 불러온다. 이러한 과정을 컨텍스트 스위칭이라고 한다.
코드를 블로킹 IO로 작성했는데, 입출력 동안 스레드가 대기하지 않고 다른 일을 할 수 있는 방법. 특별한 노려 없이 CPU 효율을 높일 수 있는 방법
가상 스레드는 실제 업무에서 쓰고자 할 때 다시 자세히 공부하자. 지금으로써는 코드를 블로킹 IO로 작성했는데 IO 병목 문제가 발생할 때 해결할 수 있는 방안 중에 하나라고 기억하자.
가상 스레드도 결국에 많아질수록 더 많은 메모리를 사용하고 스케줄리에 더 많은 시간을 사용하게 되는 문제가 있다. 사용자가 폭발적으로 증가하면 가상 스레드도 한계가 온다. 이럴 때 논블로킹 IO를 사용할 수 있다.
논블로킹 IO나 가상 스레드를 적용할 때는 먼저 다음을 검토해야 한다.