synchronized 를 이용 하여 스 레 드 동기 화 를 실현 하 는 사례 설명

1.전기 기초 지식 비축
(1)스 레 드 동기 화 정의:다 중 스 레 드 간 동기 화.
(2)다 중 스 레 드 동기 화 원인:하나의 다 중 스 레 드 프로그램 이 Runnable 인 터 페 이 스 를 통 해 이 루어 지면 클래스 의 속성 이 여러 스 레 드 에 의 해 공유 되 고 이 로 인해 자원 의 동기 화 문 제 를 야기 합 니 다.즉,여러 스 레 드 가 같은 자원 을 조작 하려 고 할 때 오류 가 발생 할 수 있 습 니 다.
(3)다 중 스 레 드 동기 화 를 실현 하 는 방식―동기 화 체 제 를 도입 한다.스 레 드 에서 하나의 자원 을 사용 할 때 자 물 쇠 를 추가 하면 다른 스 레 드 는 그 자원 에 접근 할 수 없고 자 물 쇠 를 풀 때 까지 방문 할 수 있다.이렇게 한 결과 모든 스 레 드 간 에 자원 경쟁 이 있 을 수 있 지만 모든 경쟁 자원 은 동기 화 되 고 갱신 되 며 동태 적 이 며 스 레 드 간 의 경쟁 으로 인해 자원 의'과도 한 소모'나'가상 소모'를 초래 하지 않 습 니 다.
위의 코드 는'과도 한 소모/가상 소모'문 제 를 구체 적 으로 보 여 줍 니 다.

public class TestTicketRunnable{
  public static void main(String[] a){
    TicketThread tThread = new TicketThread();
    new Thread(tThread).start();
    new Thread(tThread).start();
    new Thread(tThread).start();
  }
};
class TicketThread implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "  :ticket = " + ticket--);
      }
    }
  }
};
실행 결과:

Thread-0  :ticket = 5
Thread-2  :ticket = 5 //    
Thread-1  :ticket = 4
Thread-1  :ticket = 2
Thread-2  :ticket = 3
Thread-0  :ticket = 3 //    
Thread-0  :ticket = -1 //    
Thread-1  :ticket = 1
Thread-2  :ticket = 0
위 에서 보 듯 이 표 는 모두 5 장 이 고 세 개의 스 레 드 가 표를 사 는 것 을 호출 했다.스 레 드 1 은 인터넷 에서 매 표 1 장 을 팔 았 고 이 어 스 레 드 2 오프라인 에서 도'첫 번 째'를 팔 아'가상 소모'문제 가 발생 했다.스 레 드 3 황소 당 은 마지막 1 장의 표를 팔 았 고 스 레 드 1 인터넷 에서 마지막 1 장 을 팔 았 다.'과도 한 소모'문제 가 발생 했다.이 두 가지 문 제 는 모두 실제 생활 에서 발생 할 수 없 는 것 이다.그러나 이 3 개의 스 레 드 집행 에서 발생 했다.그것 은 분명 문제 가 있 을 것 이다.문제 의 원인 은 바로 세 채널 의'매표원'이 한 채널 에서 일 을 하지 않 는 다 는 것 이다.또는 서로 동기 화 되 지 않 고 공 유 된 자원 이 없 기 때문에 이 문 제 를 야기 하 는 근본 적 인 원인 은 바로 상호 간 의 실현 방식 이 동기 화 되 지 않 는 것 이다.
2.synchronized 를 사용 하여 동기 화 체 제 를 실현 합 니 다.
synchronized 키워드:자바 언어의 키 워드 는 대상 과 방법 또는 코드 블록 에 자 물 쇠 를 추가 할 수 있 습 니 다.하나의 방법 이나 코드 블록 을 잠 글 때 같은 시간 에 최대 하나의 스 레 드 만 이 코드 를 실행 할 수 있 습 니 다.
두 개의 병렬 스 레 드 가 같은 대상 object 의 이 잠 금 동기 코드 블록 에 접근 할 때 한 시간 동안 하나의 스 레 드 만 실 행 될 수 있 습 니 다.다른 스 레 드 는 현재 스 레 드 가 이 코드 블록 을 실행 한 후에 야 이 코드 블록 을 실행 할 수 있 습 니 다.
그것 은 두 가지 용법 을 포함한다.synchronized 방법 과 synchronized 블록 이다.
즉,스 레 드 간 동기 화 를 실현 하 는 방식 은 두 가지 가 있다.
① synchronized 동기 코드 블록 사용 하기;
② synchronized 키 워드 를 사용 하여 synchronized()를 만 드 는 방법
아래 는 각각 분석 하여 위 에서 표를 파 는 코드 를 개조 합 니 다.
① 코드―synchronized 동기 코드 블록 사용

class TicketThread implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      synchronized(this){
        if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "  :ticket = " + ticket--);
      }
      }
    }
  }
}
② 코드―synchronized 키 워드 를 사용 하여 synchronized()방법 을 만 듭 니 다.

class TicketThreadMethod implements Runnable {
  private int ticket = 5;
  public void run(){
    for (int i = 0; i < 5; i++){
      this.sale();
    }
  }
  public synchronized void sale(){
      if (ticket > 0){
        try {
          Thread.sleep(300);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "  :ticket = " + ticket--);
      }
  }
}
3.synchronized 방법 과 synchronized 동기 코드 블록 의 차이 점:
synchronized 동기 코드 블록 은 이 코드 블록 만 잠 겨 있 을 뿐 코드 블록 밖의 코드 는 접근 할 수 있 습 니 다.
synchronized 방법 은 굵 은 입자 의 병발 제어 로 어느 순간 하나의 스 레 드 만 이 synchronized 방법 을 실행 할 수 있 습 니 다.
synchronized 동기 화 코드 블록 은 입자 도의 병행 제어 로 블록 중의 코드 만 동기 화 할 수 있 고 코드 블록 이외 의 코드 는 다른 스 레 드 에 의 해 동시에 접근 할 수 있 습 니 다.
보충:다 중 스 레 드 동기 화 잠 금 synchronized(대상 잠 금 및 전체 잠 금)요약
1.synchronized 동기 자물쇠 도입

/*
 *      
 * */
//                  ,    "     "  
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  if(num > 0) {
  System.out.println(""+Thread.currentThread().getName()+"  "+",num= "+num--);
  Thread.sleep(1000);
  System.out.println(""+Thread.currentThread().getName()+"  ");
  }
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 }
}public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"  1");
 Thread thread2 = new Thread(myRunnable1,"  2");
 thread1.start();
 thread2.start();
 }
}

상기 사례 는 두 스 레 드 가 동기 화 되 지 않 은 방법 을 동시에 방문 하 는 것 을 설명 한다.만약 에 두 스 레 드 가 업무 대상 중의 인 스 턴 스 변 수 를 동시에 조작 하면'스 레 드 가 안전 하지 않다'는 문제 가 발생 할 수 있다.
이 를 통 해 우 리 는 synchronized 키 워드 를 도입 하여 동기 화 문 제 를 실현 합 니 다.
자바 에 서 는 synchronized 키 워드 를 사용 하여 스 레 드 동기 화 를 제어 합 니 다.synchronized 코드 세그먼트 가 여러 스 레 드 에 의 해 동시에 실행 되 지 않도록 제어 합 니 다.synchronized 는 방법 적 으로 도 코드 블록 에 사용 할 수 있 습 니 다.
2.대상 잠 금
(1)synchronized 방법(현재 대상 에 잠 금 추가)
위의 코드 를 수정 하면 run()방법 에 synchronized 키 워드 를 추가 하여 동기 화 방법 으로 바 꿉 니 다.

/*
 *     
 * */
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 this.print();
 }
 
 public synchronized void print() {
 if(this.num > 0) {
  System.out.println(""+Thread.currentThread().getName()+"  "+",num= "+num--);
  try {
  Thread.sleep(1000);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  System.out.println(""+Thread.currentThread().getName()+"  ");
 }
 }
}public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"  1");
 Thread thread2 = new Thread(myRunnable1,"  2");
 thread1.start();
 thread2.start();
 }
}  

결론:두 스 레 드 가 같은 대상 의 동기 화 방법 을 동시에 방문 할 때 스 레 드 가 안전 할 것 입 니 다.
(2)synchronized 코드 블록(대상 에 잠 금 추가)
동기 코드 블록 을 사용 하려 면 잠 글 대상 을 설정 해 야 하기 때문에 현재 대상 을 잠 글 수 있 습 니 다:this.

/*
 *      
 * */
class MyRunnable1 implements Runnable{
 private int num = 10;
 public void run() {
 try {
  synchronized (this) {
  if(num > 0) {
   System.out.println(""+Thread.currentThread().getName()+"  "+",num= "+num--);
   Thread.sleep(1000);
   System.out.println(""+Thread.currentThread().getName()+"  ");
  } 
  }
 } catch (InterruptedException e) {
  e.printStackTrace();
 }
 }
}
 
public class Test5_5{
 public static void main(String[] args) {
 MyRunnable1 myRunnable1 = new MyRunnable1();
 Thread thread1 = new Thread(myRunnable1,"  1");
 Thread thread2 = new Thread(myRunnable1,"  2");
 thread1.start();
 thread2.start();
 }
} 

(3)synchronized 자물쇠 다 중 개체

/*
 * synchronized    
 * */
class Sync{
 public synchronized void print() {
 System.out.println("print    :"+Thread.currentThread().getName());
 try {
  Thread.sleep(1000);
 } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 System.out.println("print    "+Thread.currentThread().getName());
 }
}
class MyThread extends Thread{
 public void run() {
 Sync sync = new Sync();
 sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

상기 사례 에 따 르 면 synchronized 가 여러 대상 을 잠 글 때 동기 화 작업 을 할 수 없다 는 것 을 알 수 있 습 니 다.이 를 통 해 키워드 synchronized 가 얻 은 자 물 쇠 는 모두 대상 자물쇠 이지 코드 나 방법(함수)을 자물쇠 로 삼 는 것 이 아 닙 니 다.synchronized 키 워드 를 가 진 방법 이나 synchronized 코드 블록 을 먼저 실행 하 는 스 레 드 는 이 방법 이나 이 코드 블록 이 가지 고 있 는 자물쇠 가 있 습 니 다.다른 스 레 드 는 대기 상태 만 나타 날 수 있 습 니 다.전 제 는 여러 스 레 드 가 같은 대상 에 접근 하 는 것 입 니 다.
공유 자원 의 읽 기와 쓰기 만 동기 화 되 어야 합 니 다.공유 자원 이 아니라면 동기 화 작업 이 필요 없습니다.
3.전역 잠 금
전역 잠 금 을 실현 하 는 데 는 두 가지 방식 이 있 습 니 다.
(1)synchronized 키 워드 를 static 방법 에 사용 합 니 다.
synchronized 를 static 정적 방법 에 추가 하 는 것 은 Class 류 에 자 물 쇠 를 채 우 는 것 이 고,synchronized 를 비 static 방법 에 추가 하 는 것 은 대상 에 자 물 쇠 를 채 우 는 것 입 니 다.Class 잠 금 은 클래스 의 모든 대상 인 스 턴 스 에 작용 할 수 있 습 니 다.

/*
 * synchronized  static   
 * */
class Sync{
 static public synchronized void print() {
 System.out.println("print    :"+Thread.currentThread().getName());
 try {
  Thread.sleep(1000);
 } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 System.out.println("print    "+Thread.currentThread().getName());
 }
}
class MyThread extends Thread{
 public void run() {
 Sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

(2)클래스 의 클 라 스 대상 을 synchronized 로 잠 금
synchronized(class)코드 블록 의 역할 은 synchronized static 방법의 역할 과 같 습 니 다.

/*
 * synchronized   Class    
 * */
class Sync{
 public void print() {
 synchronized (Sync.class) {
  System.out.println("print    :"+Thread.currentThread().getName());
  try {
  Thread.sleep(1000);
  } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
  }
  System.out.println("print    "+Thread.currentThread().getName());
 }
 }
}
class MyThread extends Thread{
 public void run() {
 Sync sync = new Sync();
 sync.print();
 }
}
public class Test5_5{
 public static void main(String[] args) {
 for(int i = 0; i < 3;i++) {
  Thread thread = new MyThread();
  thread.start();
 }
 }
}

이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.만약 잘못 이 있 거나 완전히 고려 하지 않 은 부분 이 있다 면 아낌없이 가르침 을 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기