[JAVA] 동기화의 모든 것 (synchronization)

Jan 03, 2024
[JAVA] 동기화의 모든 것 (synchronization)
Contents
동기화
 
스레드들은 동일한 데이터를 공유하기에 효율적 작업을 하지만! 동일한 메모리를 사용하기 때문에 2가지의 문제가 발생할 수 있습니다. 다음 두 녀석입니다.
  • 스레드 간섭(thread interference)
여러 개의 스레드가 공유된 데이터에 동시에 접근할 때 발생합니다. 예를 들어 하나의 스레드가 공유 데이터 값을 변경하고 있는 중에 다른 스레드가 끼어들면 이상한 결과가 나타납니다.
  • 메모리 불일치 오류(momory consistency error)
스레드에 따라 공유된 메모리의 값이 일치하지 않아 생기는 문제입니다.
이런 오류를 막기위한 도구가 동기화(synchronization)입니다.
 

🤔어떤 문제가 발생할 수 있나요?

하나의 객체를 여러 개의 스레드가 공유하는 프로그램을 해봅시다 예제) TestSynchro
// 정수 배열을 출력하는 클래스 class Printer{ void print(int[] arr){ for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread1 extends Thread{ Printer prn; int[] myarr = {1, 2, 3, 4, 5}; MyThread1(Printer prn) { this.prn = prn; } public void run(){ prn.print(myarr); } } class MyThread2 extends Thread{ Printer prn; int[] myarr = {10, 20, 30, 40, 50}; MyThread2(Printer prn) { this.prn = prn; } public void run(){ prn.print(myarr); } } public class TestSynchro { public static void main(String[] args) { Printer obj = new Printer(); MyThread1 t1 = new MyThread1(obj); MyThread2 t2 = new MyThread2(obj); t1.start(); t2.start(); } }
notion image
notion image
실행할 때마다 출력값이 섞이게 됩니다. MyThread1 스레드와 MyThread2 스레드가 동시에 Printer 객체에 접근해서 배열을 출력하기 때문입니다. 동기화를 사용하면 문제를 막을 수 있습니다.
 

동기화

스레드 동기화 방법

동기화는 공유된 자원 중에 동시에 사용하면 안 되는 자원을 보호하는 도구입니다. 여러 방법 중 한 번에 하나의 스레드만 사용할 수 있게 하는 방법이 있습니다. 하나의 스레드가 사용하고 끝날 때까지 다른 스레드가 사용하지 못하게 합니다. 임계 영역(critical section)이라고 합니다.
 
자바에선 3사지의 동기화 방식을 제공합니다.
  • 동기화 메소드(synchronized method)
  • 동기화 블록(synchronized block)
  • 정적 동기화(static synchronization)
 

동기화 메소드 사용 예제

동기화 메소드는 synchronized입니다. 공유 멧드 앞에 붙이면 되는데요. 위의 TestSynchro 예제를 수정해 보기로 하죠🔨
class Printer{ synchronized void print(int[] arr){ // 수정한 부분 synchronized for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread1 extends Thread{ Printer prn; int[] myarr = {1, 2, 3, 4, 5}; MyThread1(Printer prn) { this.prn = prn; } public void run(){ prn.print(myarr); } } class MyThread2 extends Thread{ Printer prn; int[] myarr = {10, 20, 30, 40, 50}; MyThread2(Printer prn) { this.prn = prn; } public void run(){ prn.print(myarr); } } public class TestSynchro { public static void main(String[] args) { Printer obj = new Printer(); MyThread1 t1 = new MyThread1(obj); MyThread2 t2 = new MyThread2(obj); t1.start(); t2.start(); } }
순서대로 출력되는 걸 볼 수 있어요
순서대로 출력되는 걸 볼 수 있어요
  • 먼저 동기화된 메소드는 동시 호출되더라도 마이크로 단계들이 겹치지 않습니다. 하나의 스레드가 동기화된 메소드를 실행하고 있으면, 종료할 때까지 다른 스레드는 중지됩니다. 스레드 간섭 문제를 해결합니다.
  • 동기화된 메소드가 종료되면 자동적으로 이후의 메소드 호출은 변경된 상태만을 볼 수 있습니다. 메모리 불일치 오류 문제를 해결합니다.
 

동기화 블록 사용

동기화된 블록은 메소드의 특정 리소스에 대한 동기화를 수행하는 데 사용할 수 있습니다. 메소드의 일부의 코드만 동기화하려는 경우, 동기화된 블록을 사용할 수 있다고 가정합니다. 동기화된 블록에 메소드의 모든 코드를 넣으면 동기화된 메소드와 동일하게 작동합니다.
Share article

MiracleCoding