Java 다중 스레드 프로그래밍에서의 스레드 동기화 방법

1. 다중 스레드 동기화:
1.1, 동기화 메커니즘:
다중 스레드에서 여러 개의 스레드가 유한한 자원에 접근하려고 시도할 수 있으므로 반드시 이런 상황의 발생을 예방해야 한다.그래서 동기화 메커니즘을 도입했다. 루트가 하나의 자원을 사용할 때 이를 잠그면 다른 루트는 그 자원에 접근할 수 없고 잠금이 해제된 후에야 접근할 수 있다.
1.2 공유 구성원 변수의 예:
멤버 변수와 로컬 변수:
구성원 변수:
만약 하나의 변수가 구성원 변수라면, 여러 개의 라인이 같은 대상의 구성원 변수를 조작하고, 이 여러 개의 라인은 하나의 구성원 변수를 공유하는 것이다.
로컬 변수:
만약 변수가 국부 변수라면, 여러 개의 스레드가 같은 대상을 조작하고, 모든 스레드에는 이 국부 변수의 복사가 있을 것이다.그들 사이의 국부 변수는 서로 영향을 주지 않는다.
다음 예제에서는 다음과 같이 설명합니다.
Runnable 스레드 클래스 구현:

class MyThread3 implements Runnable{

 // , 
 //int i;
 @Override
 public void run() {
  // , 
  int i = 0;
  while(i<100){
   System.out.println(i);
   i++;
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }
}

main 메서드에서 두 스레드로 동일한 객체를 조작합니다.

public static void main(String[] args) {

 MyThread3 myThread = new MyThread3();
 // (Runnable ) 
 Thread thread = new Thread(myThread);
 Thread thread2 = new Thread(myThread);
 // , , 200 
 thread.start();
 thread2.start();
}

여기서 i를 구성원 변수로 바꾸면 100개의 숫자를 출력합니다.
1.3, 자원 공유로 인한 읽기 오류
다음 예를 들어 두 라인이 하나의 Number 대상을 공용하여 Number 클래스의 getNumber 방법을 통해 데이터를 얻고 데이터를 읽고 개작할 때 중복 읽기 동작을 발견합니다.
먼저 Number 클래스를 만듭니다.

class Number{
 private int number = 10;
 public String getNumber(int i){
  if(number > 0){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   number -= i;
   return " "+i+" , :"+number;
  }
  return " "+i+" , :"+number;
 }
}
스레드 클래스, 온라인 스레드 클래스의 개인 속성은 Number 클래스의 참조를 포함합니다.

class MyThread4 extends Thread{

 // , 
 Number number;
 public MyThread4(Number number){
  this.number = number;
 }
 @Override
 public void run() {
  System.out.println(number.getNumber(8));
 }
}

main 함수에 두 개의 스레드 클래스를 만들고 같은 Number 클래스 인스턴스에 대한 참조를 포함합니다.

public static void main(String[] args) {

 Number number = new Number();
 // , number number
 MyThread4 myThread = new MyThread4(number);
 MyThread4 myThread2 = new MyThread4(number);
 myThread.start();
 myThread2.start();
}

이렇게 하면 첫 번째 스레드가 Number의number 변수를 읽을 때 먼저 저장하고 0.1초 동안 휴면한 다음에 두 번째 스레드가 number 변수를 읽고 저장한다. 이때 두 스레드는 같은 숫자를 저장하고 수정할 때 같은 숫자를 두 번 수정한다.
2. 동기화 메커니즘의 실현:
다중 스레드 병발 프로그래밍에서 Synchronized는 원로급 역할로 많은 사람들이 중량급 자물쇠라고 부르지만 자바 SE1.6에서 Synchronized에 대해 여러 가지 최적화를 한 후에 어떤 경우에는 그렇게 무겁지 않다."
Java의 각 객체는 잠금으로 사용할 수 있습니다.
동기화 방법의 경우, 자물쇠는 현재 실례 대상이다.
정적 동기화 방법의 경우 자물쇠는 현재 대상의 Class 대상입니다.
동기화 메소드 블록의 경우 자물쇠는 Synchonized 괄호 안에 구성된 객체입니다.
동기화 코드 블록에 접근하려고 할 때, 먼저 자물쇠를 받아야 하고, 종료하거나 이상을 던질 때 자물쇠를 풀어야 한다.
2.1 synchronized 키워드를 사용하여 synchronized 방법을 만듭니다.
synchronized 키워드를 사용합니다. 이 키워드를 수식하는 방법을 동기화 방법이라고 합니다.
자바에는 모든 대상에 자물쇠가 있거나 모니터라고 부른다. 어떤 대상의synchronized 방법에 접근할 때 이 대상을 자물쇠로 잠그는 것이 아니라 자물쇠를 잠그는 것을 의미한다.
이렇게 하면 대상의synchronized 방법이 특정한 라인에서 실행될 때, 다른 라인은 이 대상의synchronized 방법에 접근할 수 없습니다. (단, 다른 비synchronized 방법을 호출할 수 있습니다.)이 synchronized 방법이 실행될 때까지.
정적 synchronized 방법 호출 상황:
대상의 정적synchronized 방법을 호출할 때, 대상은synchronized 방법이 있는 대상이 아니라synchronized 방법이 있는 대상에 대응하는 Class 대상이다.이렇게 하면 다른 라인은 이 종류의 다른 정적synchronized 방법을 호출할 수 없지만, 비정적synchronized 방법을 호출할 수 있다.
결론: 정적 synchronized 방법 잠금 방법이 있는 대상, 비정적 synchronized 방법 잠금 방법이 있는 대상에 대응하는 Class 대상을 실행합니다.
다음은 다중 스레드 호출 정적 방법의 예입니다. 방법이 있는 대상에 대응하는 클래스 대상을 잠갔기 때문에 다른 스레드는 이 방법이 있는 대상에 다른 정적 synchronized 방법을 호출할 수 없습니다.

/**
 *  , 
 */
class Compute1{
 // ,
 // synchronized class ,
 // synchronized 
 public synchronized static void execute(){
  for(int i = 0; i<100; i++){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("compute1:execute1 " + i++);
  }
 }
 public synchronized static void execute2(){
  for(int i = 0; i<100; i++){
   try {
    Thread.sleep(100);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   System.out.println("compute1:execute2 " + i++);
  }
 }
}
main 방법에서 두 라인은 각각 같은 대상의 두 static synchronized 방법을 호출합니다.

public static void main(String[] args) {
 Compute1 com = new Compute1();
 Thread thread1 = new Thread1(com);
 Thread thread2 = new Thread2(com);
 thread1.start();
 thread2.start();
}
실행이 끝날 때까지 한 번에 정적 방법만 호출할 수 있습니다.
2.2, synchronized를 사용하여 동기화 코드 블록을 만듭니다.
synchronized 동기화 코드 블록을 사용하여 하나의 대상을 잠그면 이 대상은 실행 가능한 표지로 동기화 효과를 얻을 수 있습니다.

/**
 *  , 
 */
class Compute1{
 // object1 synchronized 
 private Object object1 = new Object();
 public void execute(){
  synchronized(object1){
   for(int i = 0; i<100; i++){
    try {
     Thread.sleep(100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("compute1:execute1 " + i++);
   }
  }

 }
 public synchronized void execute2(){
  synchronized(object1){
   for(int i = 0; i<100; i++){
    try {
     Thread.sleep(100);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println("compute1:execute2 " + i++);
   }
  }
 }
}

synchronized 동기화 코드 블록을 사용하여synchronized 방법과 같은 효과를 얻으려면this 인용을 잠글 수 있습니다.

synchronized(this){
 …
}
2.3. synchronized 방법과 synchronized 동기화 코드 블록의 차이점:
synchronized 동기화 코드 블록은 이 코드 블록만 잠그고 코드 블록 바깥의 코드는 접근할 수 있습니다.
synchronized 방법은 굵은 입도의 병발 제어입니다. 어느 순간에 이 synchronized 방법을 실행할 수 있는 라인만 있습니다.
synchronized 동기화 코드 블록은 세립도의 병발 제어로 블록의 코드만 동기화하고 코드 블록 이외의 코드는 다른 라인에 동시에 접근할 수 있습니다.

좋은 웹페이지 즐겨찾기