Java가 synchronized 수식 방법을 사용하여 스레드를 동기화하는 실례 설명

자바에서는 키워드synchronized를 사용하여 스레드 동기화 제어를 하고 관건적인 자원 순서 접근을 실현하여 다중 스레드 병렬 실행으로 인한 데이터 불일치성 등 문제를 피할 수 있다.synchronized의 원리는 대상 모니터 (잠금) 입니다. 모니터를 가져오는 라인만 계속 실행할 수 있습니다. 그렇지 않으면 라인이 모니터를 가져오기를 기다릴 것입니다.자바에 있는 모든 대상이나 클래스는 자물쇠와 관련이 있다. 대상에 대해 감시하는 것은 이 대상의 실례 변수이고 클래스에 대해 감시하는 것은 클래스 변수이다(한 클래스 자체는 클래스 클래스의 대상이기 때문에 클래스와 관련된 자물쇠도 대상 자물쇠이다).synchronized 키워드 사용 방식은 두 가지가 있습니다:synchronized 방법과synchronized 블록.이 두 감시 영역은 모두 하나의 도입 대상과 연결되어 있으며, 이 감시 구역에 도달하면 JVM은 이 인용 대상을 잠그고, 떠날 때 이 인용 대상의 자물쇠를 놓습니다. (이상 종료가 있으면 JVM은 자물쇠를 놓습니다.)객체 잠금은 JVM 내부 메커니즘으로 동기화 메커니즘이나 블록 동기화만 작성하면 됩니다. 모니터링 영역을 조작하면 JVM이 자동으로 잠금을 가져오거나 잠금을 해제합니다.
예제 1
먼저 첫 번째 예시를 보면 자바에서 같은 대상의 임계 구역은 같은 시간에 접근할 수 있는 것이 하나밖에 없다(비정적 synchronized 방법).

package concurrency;

public class Main8 {
  public static void main(String[] args) {
    Account account = new Account();
    account.setBalance(1000);
    Company company = new Company(account);
    Thread companyThread = new Thread(company);
    Bank bank = new Bank(account);
    Thread bankThread = new Thread(bank);
    System.out.printf("Account : Initial Balance: %f
", account.getBalance()); companyThread.start(); bankThread.start(); try { //join() companyThread.join(); bankThread.join(); System.out.printf("Account : Final Balance: %f
", account.getBalance()); } catch (InterruptedException e) { e.printStackTrace(); } } }

/* */
class Account{
  private double balance;
  /* balance */
  public synchronized void addAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp += amount;
    balance = tmp;
  }
  /* balance */
  public synchronized void subtractAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp -= amount;
    balance = tmp;
  }
  public double getBalance() {
    return balance;
  }
  public void setBalance(double balance) {
    this.balance = balance;
  }
}


/* */
class Bank implements Runnable{
  private Account account;
  public Bank(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.subtractAmount(1000);
    }
  }
}


/* */
class Company implements Runnable{
  private Account account;
  public Company(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.addAmount(1000);
    }
  }
}
너는 잔액을 충전하고 공제할 수 있는 은행 계좌의 시뮬레이션 응용 프로그램을 개발했다.이 프로그램은addAmount() 방법을 100회 호출하여 계좌를 충전하여 매번 1000씩 입금합니다.그리고 100번subtract Amount() 방법으로 계좌 잔액을 공제하고 매번 1000씩 공제한다.우리는 계좌의 최종 잔액이 초기 잔액과 같기를 기대한다. 우리는synchronized 키워드를 통해 실현되었다.
공유 데이터의 병렬 접근 문제를 보려면addAmount () 와subtractAmount () 방법 설명에 있는synchronized 키워드를 삭제하면 됩니다.synchronized 키워드가 없는 상황에서 출력된 잔액 값은 일치하지 않습니다.이 프로그램을 여러 번 실행하면 다른 결과를 얻을 수 있습니다.JVM은 스레드의 실행 순서를 보장하지 않기 때문에 매번 실행할 때마다 스레드는 서로 다른 순서로 계정의 잔액을 읽고 수정하여 최종 결과도 다르다.
하나의 대상의 방법은synchronized 키워드로 설명되며, 하나의 라인에만 접근할 수 있습니다.스레드 A가 동기화 방법인 syncmethodA () 를 실행하고 있다면, 스레드 B가 이 대상의 다른 동기화 방법인 syncmethodB () 를 실행하려면, 스레드 B는 스레드 A가 접근할 때까지 막힙니다.그러나 만약 스레드 B가 같은 종류의 다른 대상에 접근한다면 두 스레드는 모두 막히지 않을 것이다.
예제 2
같은 대상의 정적synchronized 방법과 비정적synchronized 방법이 같은 시간에 여러 라인에 접근할 수 있는 문제를 보여 주고 검증해 보세요.

package concurrency;

public class Main8 {
  public static void main(String[] args) {
    Account account = new Account();
    account.setBalance(1000);
    Company company = new Company(account);
    Thread companyThread = new Thread(company);
    Bank bank = new Bank(account);
    Thread bankThread = new Thread(bank);
    System.out.printf("Account : Initial Balance: %f
", account.getBalance()); companyThread.start(); bankThread.start(); try { //join() companyThread.join(); bankThread.join(); System.out.printf("Account : Final Balance: %f
", account.getBalance()); } catch (InterruptedException e) { e.printStackTrace(); } } }

/* */
class Account{
  /* */
  private static double balance = 0;
  /* balance , static */
  public static synchronized void addAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp += amount;
    balance = tmp;
  }
  /* balance */
  public synchronized void subtractAmount(double amount){
    double tmp = balance;
    try {
      Thread.sleep(10);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    tmp -= amount;
    balance = tmp;
  }
  public double getBalance() {
    return balance;
  }
  public void setBalance(double balance) {
    this.balance = balance;
  }
}


/* */
class Bank implements Runnable{
  private Account account;
  public Bank(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.subtractAmount(1000);
    }
  }
}


/* */
class Company implements Runnable{
  private Account account;
  public Company(Account account){
    this.account = account;
  }
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      account.addAmount(1000);
    }
  }
}
나는 단지 지난 예에서 balance에 static 키워드 수정을 추가했고addAmount () 방법도 static 키워드를 수식할 수 있다.실행 결과는 여러분 스스로 테스트해 보세요. 매번 실행할 때마다 다른 결과입니다!
요약:
  • synchronized는 소프트웨어(JVM)를 통해 이루어진 것으로 간단하고 사용하기 쉬우며 JDK5 이후에 Lock이 있어도 여전히 광범위하게 사용되고 있다
  • synchronized는 사실상 비공평하다. 새로 온 라인은 즉시 모니터를 받을 수 있고, 대기 구역에서 오랫동안 기다린 라인은 다시 기다릴 수 있지만, 이런 선점 방식은 배고픔을 예방할 수 있다
  • synchronized는 자물쇠만 하나의 조건(자물쇠를 가져오는지 여부)과 관련되어 유연하지 않다. 나중에 Condition과 Lock의 결합으로 이 문제를 해결했다..
  • 다중 스레드가 하나의 자물쇠를 경쟁할 때 나머지 자물쇠를 얻지 못한 스레드는 끊임없이 자물쇠를 얻으려고 시도할 수 있을 뿐 중단할 수 없다.높은 병발의 경우 성능이 떨어질 수 있다.ReentrantLock의 lockInterruptibly () 방법은 응답 중단을 우선적으로 고려할 수 있습니다.하나의 스레드가 너무 오래 기다리면 자신을 중단할 수 있습니다. 그리고 ReentrantLock은 이 스레드가 더 이상 기다리지 않도록 응답합니다.이 메커니즘이 있으면 ReentrantLock을 사용할 때synchronized처럼 자물쇠가 사라지지 않습니다..
  • 좋은 웹페이지 즐겨찾기