들어가기 전

이번 포스팅에서는 동시성 이슈에 대해 알아보겠습니다. 동시성 이슈를 알아보기 전에 싱글 스레드와 멀티 스레드 환경에 대해서 알아보겠습니다.

 

싱글스레드와 멀티스레드

 

싱글 스레드(Single Thread)

 

싱글 스레드는 애플리케이션이 스레드 하나로만 실행되는 것을 의미합니다.

아래와 같이 자바 애플리케이션을 실행하면 싱글 스레드로 동작을 합니다.

 

public class SingleThread {
    public static void main(String[] args) {
    
    System.out.println("싱글 스레드 실행");
}

 

싱글 스레드는 작업을 순차적으로 실행시킵니다.

멀티 스레드(Multi Thread)

멀티 스레드는 싱글 스레드와 달리 애플리케이션이 여러 개의 스레드로 실행되는 것을 의미합니다.

아래와 같이 실행을 하면 여러 개의 스레드가 순서 없이 실행합니다.

 

public class MultiThread {

	public static void main(String[] args) throws InterruptedException {
		Thread thread1 = new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " 실행 중..");
		}, "첫번째 스레드");
		Thread thread2 = new Thread(() -> {
			System.out.println(Thread.currentThread().getName() + " 실행 중..");
		}, "두번째 스레드");

		thread1.start();
		thread2.start();

		System.out.println(Thread.currentThread().getName() + " 실행중 ..");
	}
}

 

 

 

 

 

 

싱글스레드 VS 멀티 스레드

 

싱글 스레드 멀티 스레드
하나의 스레드이기때문에 컨텍스트 스위칭 X 빈번한 컨텍스트 스위칭으로 인해 성능 저하 가능성 O
동기화 이슈 X 스레드 간 동기화 이슈 O
자원 비용이 적음 스레드 생성 비용이 적지 않음
프로그래밍 난이도가 낮음 프로그래밍 난이도가 높음
CPU 멀티 코어 사용이 불가능 CPU 멀티코어의 병렬성으로 성능 향상
I/O 처리시 CPU가 낭비됨 CPU를 낭비없이 자원을 효율적으로 사용
스레드에서 에러가 발생하면 프로그램 종료됨 하나의 스레드에서 문제가 생기더라도 다른 스레드에 영향 X
순차적 실행으로 응답성 및 전체 처리량이 낮음 동시성으로 사용자에게 응답성 향상

 

지금까지 싱글 스레드와 멀티 스레드에 대해서 알아보았습니다.
이제 동시성 이슈에 대해서 알아보겠습니다.

 

동시성 이슈란?

동시성 이슈는 여러 스레드가 동시에 공유 자원에 접근하여 발생하는 문제입니다.

여기서 "여러 스레드"라는 말에서 동시성 이슈는 멀티 스레드 환경에서 발생한다는 것을 알 수 있습니다.

싱글 스레드 환경에서는 단 하나의 스레드로 애플리케이션이 실행되기 때문에 동시성 이슈가 발생할 수 없습니다.

 

예시로 싱글 스레드와 멀티 스레드 환경에 대한 동시성 이슈 발생 여부를 알아보겠습니다.

 

싱글 스레드 환경

 

public class CurrencyIssueTest {

	static int count = 0;

	@DisplayName("싱글 스레드에서 공유 자원에 접근하여도 동시성 이슈는 발생하지 않는다.")
	@Test
	void notOccurSingleThreadAccessSharedResourceCurrencyIssue() {
		for (int i = 1; i <= 25000; i++) {
			count += 1;
		}
		for (int i = 1; i <= 25000; i++) {
			count += 1;
		}
		Assertions.assertThat(count).isEqualTo(25000 * 2);
	}
}

 

 

싱글 스레드 환경에서는 기대했던 값이 나와 테스트를 성공하는 것을 확인할 수 있습니다.

 

 

멀티 스레드 환경

 

public class CurrencyIssueTest {
	static int count = 0;
	@DisplayName("여러 스레드에서 공유 자원에 접근하면 동시성 이슈가 발생한다.")
	@Test
	void occurMultiThreadAccessSharedResourceCurrencyIssue() {
		Thread thread1 = new Thread(() -> {
			for(int i = 1; i <= 25000; i++) {
				count += 1;
			}
		}, "첫번째 스레드");
		Thread thread2 = new Thread(() -> {
			for(int i = 1; i <= 25000; i++) {
				count += 1;
			}
		}, "두번째 스레드");

		thread1.start();
		thread2.start();
		Assertions.assertThat(count).isEqualTo(25000 * 2);
	}
}

 

 

싱글 스레드와 달리 멀티 스레드 환경에서는 동시성 이슈가 발생하여 원하는 결과값이 안 나와 테스트가 실패한 것을 확인할 수 있습니다.

 

 

예시에서 싱글 스레드에서는 동시성 이슈가 발생하지 않지만 멀티 스레드 환경에서는 동시성 이슈가 발생하는 것을 확인할 수 있습니다. 

 

예시로 싱글 스레드와 멀티스레드 환경에서의 동시성 이슈가 발생 여부를 확인하였습니다. 
그럼 왜 싱글 스레드 환경에서는 동시성 이슈가 발생하지 않지만 멀티 스레드 환경에서는 동시성 이슈가 발생하는지 동작 과정을 통해서 알아보겠습니다.

 

 

싱글 스레드 동작과정

 

  • 싱글 스레드는 작업을 순차적으로 진행을 합니다.
  • 첫 번째 반복문에서 초기값이 0 인 count 값에 25000번 1씩 더해서 결과값은 25000이 나왔습니다.
  • 두 번째 반복문에서는 첫 번째 반복문에서 나온 결과값에 25000번 1씩 더해서 50000이 나왔습니다.

 

멀티 스레드 동작과정

 

 

  • 1번 : "첫 번째 스레드"가 실행될 때 초기값이 0인 count에 1씩 10000번 더하여 count값이 "10000"이 되고 count의 값을 메모리에 저장을 하고 컨텍스트 스위칭이 일어나 "두 번째 스레드"가 실행이 됩니다.
  • 2번 : "두 번째 스레드"가 1번에서 실행하고 메모리에 저장된 count 값에 1씩 10000번 더하여 count의 값은 "20000"이 되고 해당 값을 메모리에 저장하고 컨텍스트 스위칭이 일어나 "첫 번째 스레드"가 실행이 됩니다.
  • 3번 :  "첫 번째 스레드"가 2번에서 실행하고 메모리에 저장된 count 값에 1씩 5000번 더하여 count의 값은 "25000"이 되지만 메모리에 저장하지 않고 컨텍스트 스위칭이 일어나 "두 번째 스레드"가 실행됩니다.
  • 4번 :  3번에서 실행한 값이 메모리에 저장이 되지 않아  "두 번째 스레드"가 2번에서 실행한 값에 1씩 15000번 더하여 count 값은 "40000"이 되고 해당 값을 메모리에 저장하고 두 번째 스레드는 종료가 되고  "첫 번째 스레드"가 실행됩니다.
  • 5번 : "첫 번째 스레드"가 4번에서 실행하고 메모리에 저장된 count 값에 1씩 10000번 더하여 count 값은 "50000"이 되지만 메모리에 저장하지 않고 스레드가 종료가 됩니다.
  • 1번 ~ 5번까지 실행한 최종 결과 값이 "40000"이 나왔습니다.

 

정리

동시성 이슈는 여러 스레드가 공유 자원에 동시에 접근하여 발생하는 것입니다. 위 예시와 동작하는 과정을 보면 싱글 스레드 환경에서는 동시성 이슈가 발생하지 않아 기대하는 결과값을 얻을 수 있었지만 멀티 스레드 환경에서는 동시성 이슈가 발생하여 기대하는 결과값을 얻을 수 없었습니다.