
스레드
장점 : 동일한 데이터를 공유하기 때문에 효율적으로 작업이 가능
단점 : 스레드 간섭(thread interference)
메모리 불일치 오류(memory consistency error)
메모리 불일치 오류(memory consistency error)
스레드에 따라서 공유된 메모리의 값이 일치하지 않는 현상

예시) 스레드 a, b, c가 있는데 화장실이라는 리소스가 1개가 있음
3개 다 화장실에 접근할 수 있음
리소스 크기가 2이고 a, b, c가 모두 1씩 더한다면 크기가 5가 됨
각각의 스레드 입장에서는 1만 더했지만 5가 나옴
내가 예상치 못한 결과를 만날 수 있음
스레드 간섭(thread interference)
package ex16; class Printer { void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } 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); } } class MyThread3 extends Thread { Printer prn; int[] myarr = {1, 2, 3, 4, 5}; MyThread3(Printer prn) { this.prn = prn; } public void run() { prn.print(myarr); } } public class TestSynchro { public static void main(String[] args) { Printer obj = new Printer(); MyThread2 t1 = new MyThread2(obj); MyThread3 t2 = new MyThread3(obj); t1.start(); t2.start(); } }


스레드들이 Printer 객체에 동시 접근 → 매번 결과가 달라짐
여러 개의 스레드가 공유된 데이터에 동시에 접근할 때 발생
동기화의 장점
: 공유된 자원 중에서 동시에 사용하면 안 되는 자원을 보호
임계 영역(critical section)
: 한 명이 사용하고 있으면 다른 사람들은 사용이 끝날 때까지 기다리는 것
동시 접근을 막아버림
락(rock)과 모니터(monitor)로 알려진 방법을 사용하여 구축됨
모든 객체에는 연결된 락이 있음
객체의 필드에 일관된 액세스가 필요한 경우 > 락 획득 > 액세스 > 완료 > 락 해
정적 동기화(static Synchronization)
동기화 메서드(Synchronized method)
package ex16; class Printer1 { synchronized void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } class MyThread21 extends Thread { Printer prn; int[] myarr = {10, 20, 30, 40, 50}; MyThread21(Printer prn) { this.prn = prn; } public void run() { prn.print(myarr); } } class MyThread31 extends Thread { Printer prn; int[] myarr = {1, 2, 3, 4, 5}; MyThread31(Printer prn) { this.prn = prn; } public void run() { prn.print(myarr); } } public class TestSynchro1 { public static void main(String[] args) { Printer obj = new Printer(); MyThread21 t1 = new MyThread21(obj); MyThread31 t2 = new MyThread31(obj); t1.start(); t2.start(); } }

: 스레드 간섭과 메모리 불일치 오류를 막는 간단한 기법
데드락(deadlock)과 아사(starvation) 문제를 해결할 수 없음
동기화 블록(Synchronized block)
package ex16; class Printer2 { void print(int[] arr) throws Exception { synchronized (this) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class MyThread22 extends Thread { Printer prn; int[] myarr = {10, 20, 30, 40, 50}; MyThread22(Printer prn) { this.prn = prn; } public void run() { prn.print(myarr); } } class MyThread32 extends Thread { Printer prn; int[] myarr = {1, 2, 3, 4, 5}; MyThread32(Printer prn) { this.prn = prn; } public void run() { prn.print(myarr); } } public class TestSynchro2 { public static void main(String[] args) { Printer obj = new Printer(); MyThread22 t1 = new MyThread22(obj); MyThread32 t2 = new MyThread32(obj); t1.start(); t2.start(); } }

: 메서드의 특정 리소스에 대한 동기화를 수핼할 때 사용
동기화된 블록에 메서드의 모든 코드 넣기
→ 동기화된 메서드와 동일하게 작동 함
동기화의 단점
: 대기하는 스레드들이 많아지만 JVM이 느려지거나 일시 중단 되기도 함
기아(starvation), 라이브락(livelock)
교착 상태(deadlock)
package ex16; public class DeadLockTest { public static void main(String[] args) { final String res1 = "Gold"; final String res2 = "Sliver"; Thread t1 = new Thread(() -> { synchronized (res1) { System.out.println("Thread 1: 자원 1 획득"); try { Thread.sleep(100); } catch (Exception e) { } synchronized (res2) { System.out.println("Thread 1: 자원 2 획득"); } }}); Thread t2 = new Thread(() -> { synchronized (res2) { System.out.println("Thread 2: 자원 2 획득"); try { Thread.sleep(100); } catch (Exception e) { } synchronized (res1) { System.out.println("Thread 2: 자원 1 획득"); } }}); t1.start(); t2.start(); } }

: 두 스레드가 상대방이 가진 락을 기다리고 있기 때문에 대기하게 됨

예시) 스레드 a, b, c가 있는데 화장실이라는 리소스가 1개가 있음
동시에 세개가 접근하면 교착상태가 일어남
화장실이라는 자원의 특성임
세 개가 낑겨서 아무것도 못하는 것이 데드락 > 프로그램이 뻗음
> 재부팅해야 함
그래서 동시 접근을 막아야 함
해결방법
package ex16; public class DeadLockTest1 { public static void main(String[] args) { final String res1 = "Gold"; final String res2 = "Sliver"; Thread t1 = new Thread(() -> { synchronized (res1) { System.out.println("Thread 1: 자원 1 획득"); try { Thread.sleep(100); } catch (Exception e) { } synchronized (res2) { System.out.println("Thread 1: 자원 2 획득"); } }}); Thread t2 = new Thread(() -> { synchronized (res1) { System.out.println("Thread 2: 자원 1 획득"); try { Thread.sleep(100); } catch (Exception e) { } synchronized (res2) { System.out.println("Thread 2: 자원 2 획득"); } }}); t1.start(); t2.start(); } }

: 잠금 순서 변경
Share article