Java 39(스레드 동기화 Thread Synchronized)

Kang.__.Mingu·2024년 5월 12일
0

Java

목록 보기
41/108

다중 스레드 프로그램의 문제점

  • 같은 클래스로 생성된 스레드가 동일한 메소드를 동시에 요청한 경우 스레드로 동작될 명령이 순서없이 실행되어 잘못된 처리 결과 발생 가능
  • 해결법) 스레드를 동기화 처리하여 스레드에 대한 메소드 호출 제어

스레드 동기화(Thread Synchronized)

  • 스레드가 메소들를 호출한 경우 메소드의 모든 명령을 실행하기 전까지 다른 메소드를 호출한 다른 스레드로 명령을 실행하지 못하도록 스레드 잠금(Lock) 기능 제공
  • 메소드 잠김 상태인 경우 메소드를 호출한 다른 스레드는 일시 중지 상태로 변환하고 메소드 잠금 상태가 해제되면 스레드로 메소드의 명령 실행

메소드 호출에 대한 스레드 동기화 처리 방법(중요!)

1.synchronized 키워드를 사용하여 메소드 작성 - 동기화 메소드(synchronized Method)

형식) 접근제한자 synchronized 반환형 메소드명(자료형 변수명, ...){명령; ...}

  1. synchronized 키워드로 블럭을 설정하여 메소드 호출

형식) synchronized(공유 객체) {객체.메소드명(값, ...); ...}

  • synchronized 키워드로 제공되는 객체로 호출되는 모든 메소드는 동기화 처리되어 실행

은행계좌 프로그램(동시에 접근했을 때)

Account(첫번째 동기화 방법)

// 은행계좌정보(잔액)를 저장하기 위한 클래스
// => 입금 및 출금 관련 메소드 작성
public class Account {
    private int balance;

    public Account() {
    }

    public Account(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    // 입금 처리 메소드 - 매개변수로 입금자와 입금액을 전달받아 처리
    // 스레드 동기화 처리(synchronized) 1번 방법
    public synchronized void deposit(String name, int ammount) {
        balance += ammount;
        System.out.println("[입금] " + name + "님이 " + ammount + "원을 입금하여 잔액은 " + balance + "원 입니다.");
    }

    // 출금 처리 메소드 - 매개변수로 출금자와 출금액을 전달받아 처리
    public void withdraw(String name, int ammount) {
        if(balance < ammount){
            System.out.println("[에러] " + name + "님 " + "잔액이 " + balance + "원 남아 " + ammount + "원을 출금할 수 없습니다.");
            return;
        }
        balance -= ammount;
        System.out.println("[출금] " + name + "님이 " + ammount + "원을 출금하여 잔액은 " + balance + "원 입니다.");
    }
}

AccountUser(두번째 동기화 방법)

// 은행계좌를 사용할 수 있는 사용자정보(은행계좌정보, 사용자명)를 저장하기 위한 클래스
public class AccountUser extends Thread {
    // 생성자 또는 Setter 메소드로 필드에 객체를 저장하여 포함관계 완성
    private Account account;
    private String userName;

    public AccountUser(Account account, String userName) {
        this.account = account;
        this.userName = userName;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    // 새로운 스레드로 실행될 명령 작성 - 입금 또는 출금 관련 명령 작성
    @Override
    public void run() {
        // 스레드 동기화 1번 방법)(메소드에서 처리)
        // account.deposit(userName, 5000);

        // 스레드 동기화 2번 방법
        synchronized (account){ // 메소드에서 synchronized가 안 되어있을 때
            account.withdraw(userName, 5000);
        }

        // Thread.sleep() 사용 할 경우
        // try {
        //     // InterruptedException 발생할 수 있기 때문에 try catch로 예외처리
        //     Thread.sleep(500);
        // } catch (InterruptedException e) {
        //     // throw new RuntimeException(e);
        //     e.printStackTrace();
        // }
    }
}

AccountUserApp

public class AccountUserApp {
    public static void main(String[] args) {
        // Account 클래스로 객체를 생성하여 참조변수에 저장 - 은행 게좌 생성
        Account account = new Account(10000);

        // AccountUser 클래스로 객체를 생성하여 참조변수에 저장 - 동일한 은행계좌를 사용할 수 있는 다수의 사용자 생성
        AccountUser userOne = new AccountUser(account, "홍길동");
        AccountUser userTwo = new AccountUser(account, "임꺽정");
        AccountUser userThree = new AccountUser(account, "전우치");
        
        /*
        // 사용자(AccountUser 객체)로부터 은행계좌(Account 객체)를 제공받아 입금 처리(메소드 호출)
        // => 단일스레드(main 스레드)를 이용해 객체를 참조해 메소드 호출
        // => 단일스레드를 사용하므로 동시 입금 처리 불가능
        userOne.getAccount().deposit(userOne.getUserName(), 5000);
        userTwo.getAccount().deposit(userTwo.getUserName(), 5000);
        userThree.getAccount().deposit(userThree.getUserName(), 5000);
        */

        // 새로운 스레드를 생성하여 AccountUser 클래스의 run() 메소드에 명령 실행
        // new AccountUser(account, "홍길동").start();
        // new AccountUser(account, "임꺽정").start();
        // new AccountUser(account, "전우치").start();

        userOne.start();
        userTwo.start();
        userThree.start();

    }
}
profile
최선을 다해 꾸준히 노력하는 개발자 망고입니당 :D

0개의 댓글