여러 스레드가 동시에 접근하거나 사용하는 자원으로, 메모리, 변수, 파일, 데이터베이스 연결 등이 포함됩니다.
여러 스레드가 동시에 자원에 접근하여 예기치 않은 결과가 발생하는 상황을 말합니다. 스레드가 경쟁적으로 자원에 접근하면서, 올바르지 않은 값이 저장되거나 데이터가 손상될 가능성이 있습니다.
정의: 여러 스레드가 동시에 공유 자원에 접근하지 못하도록 제어하는 메커니즘입니다. 동기화를 통해 한 번에 하나의 스레드만 자원에 접근할 수 있게 하여 데이터 불일치 문제를 방지할 수 있습니다.
synchronized
키워드를 사용하여 메서드나 블록을 동기화하면, 해당 부분을 한 번에 한 스레드만 접근할 수 있습니다.예를 들어, 은행의 계좌를 관리하는 프로그램을 만든다고 가정해 봅시다. 여기에는 계좌(Account) 클래스가 있고, 두 명의 고객(Client)이 동시에 출금을 시도합니다. 각 고객이 계좌에서 일정 금액을 출금하는 작업을 각각 다른 스레드로 처리하게 됩니다. 계좌의 잔고가 1000원인데, 두 고객이 동시에 800원을 출금하려고 하면 어떤 일이 발생할까요?
예제코드
package com.exam7;
public class Account {
// 통장 잔고 (공유 자원)
private int balance = 1000;
// 현재 잔고를 반환하는 메서드
public int getBalance() {
return balance;
}
// 출금 메서드
// public void withdraw(int money) { // 경쟁 상태 발생
public synchronized void withdraw(int money) { // synchronized 통해 경쟁상태 문제 해결
// 잔고가 출금 요청 금액보다 많거나 같은지 확인
if (balance >= money) {
// 실제 출금 처리
try {
Thread.sleep(1000); // 출금 처리 전 1초 대기 (예: 실제 출금 작업을 시뮬레이션)
balance -= money; // 잔고에서 출금 금액을 차감
} catch (InterruptedException e) {
System.out.println("[에러] " + e.getMessage()); // 예외 발생 시 에러 메시지 출력
}
} else {
System.out.println("잔고가 없음");
}
}
}
package com.exam7;
public class Client implements Runnable {
// 출금할 계좌
private Account account;
// 생성자 - 특정 계좌 객체를 받아 저장
public Client(Account account) {
this.account = account;
}
@Override
public void run() {
// 계좌 잔고가 0보다 클 때까지 출금 시도
while (account.getBalance() > 0) {
// 출금할 금액을 100~400 사이의 랜덤 값으로 설정
int money = (int) (Math.random() * 3 + 1) * 100;
account.withdraw(money); // 출금 수행
// 현재 잔고 출력
System.out.println("통장 잔고 : " + account.getBalance());
}
}
}
package com.exam7;
public class ClientEx {
public static void main(String[] args) {
Account account = new Account();
Client client1 = new Client(account);
Client client2 = new Client(account);
Thread t1 = new Thread(client1);
Thread t2 = new Thread(client2);
System.out.println("시작");
// 2개의 ATM 동시 인출
t1.start();
t2.start();
System.out.println("끝");
}
}