[Java] synchronized 는 무엇이며 어떤 방식으로 작동할까?

2024. 2. 1. 11:10·Java

자바에서 우리는 synchronized 라는 키워드를 쉽게 만날 수 있다.
이러한 키워드를 왜 사용하며 어떤 방식으로 사용하면 좋을 지에 대한 것을 정리해보겠다.

이 내용의 일부 자바 성능 튜닝이라는 책을 참고하여 작성하였다.

이번에는 사용법에 대한 내용을 정리하겠지만, 나중에는 성능에 대한 것도 정리 예정이다.

synchronized 란?


동시에 어떤 작업에 접근을 하게 되었을 때 접근을 제어하는 역할을 하는 키워드 이다.

예를 들어 진행을 해보겠다.

  1. 하나의 기부를 받는 단체 클래스가 있다고 해보자. 이러한 단체에 동시에 여러 명의 사람(기부를 하는 사람) 이 기부를 하게 되었을 때 어떤 결과가 나오게 될까?
  • 기부 단체 클래스
//기분 단체 클래스
public class Contribution {  
    private int amount = 0;  

    public void donate(){  
        amount++;  
    }  
    public int getTotal(){  
        return amount;  
    }  
}
  • 기부 하는 사람 클래스
public class Contributor extends Thread {  

    private Contribution myContribution;  

    private String myName;  

    public Contributor(Contribution myContribution, String myName) {  
        this.myContribution = myContribution;  
        this.myName = myName;  
    }  

    @Override  
    public void run() {  
        for (int i = 0; i < 1000; i++) {  
             myContribution.donate();  
        }  
        System.out.format(" %s total = %d\n" , myName,myContribution.getTotal());  
    }  
}
  • 테스트 클래스
public class ContributeTest {  
    public static void main(String[] args) {  

        Contributor[] contributors = new Contributor[10];  
        Contribution contribution = new Contribution();  
        for (int i = 0; i < 10; i++) {  
            contributors[i] = new Contributor(contribution, " Contributor" + i);  

        }  

        for(int loop= 0 ; loop <10 ; loop++){  
            contributors[loop].start();  
        }  
    }  
}

결과 코드
  Contributor2 total = 2181
  Contributor3 total = 3181
  Contributor1 total = 2181
  Contributor0 total = 2181
  Contributor4 total = 4181
  Contributor5 total = 5181
  Contributor6 total = 6181
  Contributor7 total = 7181
  Contributor8 total = 8181
  Contributor9 total = 9181
  • 결과를 보면 알겠지만, 분명 우리는 10명의 사람이 동시에 1개의 단체에 동시에 접근을 해서 기부를 하게 되었다.
  • 상식적으로 생각을 해보면, 알 수 있지만 한 명 당 1000원씩 10명이 1개의 단체에 기부를 한 것이기 때문에 10000원이 되는 것이 맞다고 할 수 있다.
  • 하지만 우리의 결과와는 다르게 9181원이라는 결과가 나오게 되었다.
    이게 무슨 일일까?

Thread 의 동시 접근


스레드란 무엇인지 부터 생각을 해보아야 한다.

  • 스레드는 하나의 프로세스(프로그램에) 여러 개의 스레드를 만들고 접근할 수 있게 해주는 방법이다.
  • 스레드는 다른 말로 Lightweight Process(LWP) 가벼운 프로세스라고도 한다.
  • 즉, 가벼운 프로세스이며, 프로세스에서 만들어 사용하고 있는 메모리를 공유한다.
  • 그래서 별개의 프로세스가 하나 씩 뜨는 것보다는 성능이나 자원 사용에 있어서 많은 도움이 된다.

다시 보면 알겠지만 기부자를 나타내는 Contributor 클래스는 Thread로 구현이 되어있다.
이 Contributor는 public void run()를 실행하게 되었을 때 Contribution 동시에 접근을 할 수 있다.

public class Contributor extends Thread {  

    private Contribution myContribution;  

    private String myName;  

    public Contributor(Contribution myContribution, String myName) {  
        this.myContribution = myContribution;  
        this.myName = myName;  
    }  

    @Override  
    public void run() {  
        for (int i = 0; i < 1000; i++) {  
             myContribution.donate();  
        }  
        System.out.format(" %s total = %d\n" , myName,myContribution.getTotal());  
    }  
}

그럼 이게 왜 문제일까?


그냥 동시에 접근하면 하면 되는 거 아닌가? 라고 생각할 수 있지만 이게 문제가 될 수 있다.

//기분 단체 클래스
public class Contribution {  
    private int amount = 0;  

    public void donate(){  
        amount++;  
    }  
    public int getTotal(){  
        return amount;  
    }  
}

사실 amount++ 연산은 사실상 세 가지 단계로 이루진다.

  • 현재 값 읽기 (Read): 현재 변수의 값을 읽어온다.
  • 값 증가 (Increment): 읽어온 값에 1을 더한다.
  • 결과로 새로운 값을 저장 (Write): 새로운 값을 변수에 저장한다.

여러 스레드에서 동시에 donate 메서드를 호출할 때, 두 스레드가 동시에 amount++을 수행하면 아래와 같은 문제가 발생할 수 있다.

  • 두 스레드가 동시에 현재 값을 읽어와서 각각 1을 더하고, 그 결과를 저장하려고 할 때, 예상치 못한 동작이 발생할 수 있습니다.
  • 두 스레드가 동시에 읽어온 값이 동일하면, 실제로 증가된 값은 1이 아니라 2가 되어야 하지만, 이러한 예상치 못한 결과가 발생할 수 있다.
  • 이게 무슨 말이나면 동시에 접근하게 되었을 때 두 스레드가 동시에 3을 읽었다고 해보자.
    각자 읽어 온 값이 3이기 때문에 4로 증가 시키고 저장을 하면 사실 5가 되어야 하는 값이 1만 증가해서 4 라는 숫자를 저장하기 때문에 동시성에 대한 문제가 생긴다는 것이다.

이러한 이유로 여러 스레드에서 동시에 값을 변경하는 경우, 즉각적으로 읽고 쓰는 연산에 대해서는 동기화를 사용하는 것이 좋다.

따라서 위의 문제를 해결하기 위해서 다시 코드를 작성해 보겠다.

예시 코드 수정 후 완성

  • 기부자 코드
public class Contributor extends Thread {  

    private Contribution myContribution;  

    private String myName;  

    public Contributor(Contribution myContribution, String myName) {  
        this.myContribution = myContribution;  
        this.myName = myName;  
    }  

    @Override  
    public void run() {  
        for (int i = 0; i < 1000; i++) {  
             myContribution.donate();  
        }  
        System.out.format(" %s total = %d\n" , myName,myContribution.getTotal());  
    }  
}
  • 기부 단체 코드 synchronized 키워드를 붙여 주었다.
public class Contribution { 
	private int amount = 0; 
	public synchronized void donate(){ 
		amount++; 
	}
	public int getTotal(){  
    	return amount;  
	}
}

 

  • 테스트 코드


```java
public class ContributeTest {  

    public static void main(String[] args) {  

        Contributor[] contributors = new Contributor[10];  
        Contribution contribution = new Contribution();  
        for (int i = 0; i < 10; i++) {  
            contributors[i] = new Contributor(contribution, " Contributor" + i);  

        }  

        for(int loop= 0 ; loop <10 ; loop++){  
            contributors[loop].run();  
        }  
    }  
}
  • 결과를 실행해본 결과 10000원 이라는 결과를 나타내었다.
  • 사실 여기서 Thread 에 대한 동작 방식, 성능을 더 공부하면 좋지만 여기서는 다루지 않겠다.
 Contributor0 total = 2687
  Contributor2 total = 6587
  Contributor1 total = 6886
  Contributor4 total = 6907
  Contributor7 total = 8787
  Contributor3 total = 8963
  Contributor8 total = 9107
  Contributor5 total = 9519
  Contributor6 total = 9759
  Contributor9 total = 10000

이렇게 synchronized 를 사용해 동시성 문제를 해결하는 것을 알아보았다.

끝

추가 문법 정리


동기화된 메서드: 메서드를 동기화하려면 메서드 선언에 synchronized 키워드를 추가합니다. 스레드가 동기화된 메서드에 진입하면 해당 메서드의 객체에 연결된 락을 얻어 다른 스레드가 동일한 객체의 어떤 동기화된 메서드에도 진입하지 못하게 합니다.

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

동기화된 블록: 메서드 내에서 동기화된 블록을 사용하여 특정 코드 섹션을 보호할 수 있습니다. 이 방법은 전체 메서드가 아닌 메서드 일부만 동기화하려는 경우 유용합니다. 동기화된 블록을 만들려면 락으로 사용할 객체를 지정하고 한 번에 한 스레드만 락을 보유할 수 있습니다.

public void 어떤메서드() {
    // 비동기화된 코드

    synchronized (락객체) {
        // 동기화된 코드 블록
    }
}
반응형

'Java' 카테고리의 다른 글

[JAVA] JAVA 네이티브 언어와 통신하기 위한 JNI 개념  (0) 2024.08.11
Java - array, arrayList 에서 크기 가변의 내부 동작  (0) 2023.12.09
Java - LinkedList (feat. vs ArrayList)  (0) 2023.12.03
interface기반 구현(프로그래밍) 이란?  (0) 2023.11.29
String, StringBuilder, StringBuffer  (0) 2023.11.20
'Java' 카테고리의 다른 글
  • [JAVA] JAVA 네이티브 언어와 통신하기 위한 JNI 개념
  • Java - array, arrayList 에서 크기 가변의 내부 동작
  • Java - LinkedList (feat. vs ArrayList)
  • interface기반 구현(프로그래밍) 이란?
코딩 코딩 코오딩
코딩 코딩 코오딩
  • 코딩 코딩 코오딩
    코딩하는 누누
    코딩 코딩 코오딩
  • 전체
    오늘
    어제
    • 분류 전체보기 (491)
      • 생산성 (2)
        • 인텔리제이 (2)
      • 프로젝트 기록 (14)
        • git (2)
        • spring (3)
        • TestCode (2)
        • spring security (3)
        • 기타 (2)
        • MySQL (0)
        • Cloud (2)
      • 회고 (4)
      • Spring (6)
      • JPA (0)
      • DB (4)
        • MySql (2)
        • Redis (1)
      • Java (7)
        • JSP (1)
      • 잡담 (1)
      • CS (30)
        • 컴퓨팅 사고 (0)
        • 배열 (4)
        • 알고리즘 (8)
        • 메모리 (7)
        • 자료구조 (9)
        • 암호학 (2)
      • opencv (14)
      • AI (56)
        • 머신러닝 (2)
        • 딥러닝 (7)
        • tensorflow (3)
        • 머신러닝(딥러닝) 정리 (21)
        • 강화학습 (7)
        • 논문 읽기 (1)
        • 잡동사니 (1)
        • python AI (13)
        • 선형대수 (1)
        • 확률론 (0)
      • 알고리즘 공부 (177)
        • 그래프 이론 (0)
        • 다익스트라 (4)
        • 위상정렬 (3)
        • 신장트리-크루스칼 알고리즘 (4)
        • 플로이드 워셜 (3)
        • 이진탐색 (9)
        • 백트래킹 (11)
        • 부르드포스 (9)
        • 다이나믹 프로그래밍 (20)
        • BFS & DFS (24)
        • 그리디 (6)
        • 구현 (15)
        • 정렬 (3)
        • 기타 (62)
        • 수학? (1)
      • 코딩 (173)
        • 파이썬(python) (15)
        • c언어 (13)
        • 프로그래머스 lv1 (46)
        • 프로그래머스 lv2 (41)
        • 백준 - c++ (49)
        • Softeer (9)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자료구조
    다이나믹 프로그래밍
    인접리스트
    프로그래머스
    코딩문제
    BFS
    소수찾기
    그리디
    캘리브레이션
    알고리즘
    c언어
    if문
    코딩기초스킬
    정렬
    C언어 기초
    인접행렬
    코딩테스트
    순차 탐색
    스택
    DFS
    왜곡보정
    큐
    에라토슽네스의 체
    삽입 정렬
    백준
    코딩기초
    코딩
    n진법 변환
    이미지처리
    선택정렬
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
코딩 코딩 코오딩
[Java] synchronized 는 무엇이며 어떤 방식으로 작동할까?
상단으로

티스토리툴바