java 다중 루틴 프로그래밍의 Synchronized 블록 동기화 방법

글은synchronized에 대한 네 가지 예를 공유했다
1. synchronized 키워드를 추가할지 여부의 차이

public class ThreadTest {

  public static void main(String[] args) {
    Example example = new Example();

    Thread t1 = new Thread1(example);
    Thread t2 = new Thread1(example);
    t1.start();
    t2.start();
  }
}

class Example {
  public synchronized void excute() {
    for (int i = 0; i < 5; ++i) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("excute:" + i);
    }

  }

}

class Thread1 extends Thread {
  private Example example;

  public Thread1(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute();
  }
}

synchronized 키워드를 추가한 출력 결과는 다음과 같습니다.
0~4 조를 먼저 출력하고 다음 조를 출력하며 두 개의 스레드 순서로 실행합니다
excute:0
excute:1
excute:2
excute:3
excute:4
excute:0
excute:1
excute:2
excute:3
excute:4
synchronized 키워드를 추가하지 않은 출력 결과는 다음과 같습니다.
두 라인이 동시에 excute 방법을 실행하고, 동시에 병행한다
excute:0
excute:0
excute:1
excute:1
excute:2
excute:2
excute:3
excute:3
excute:4
excute:4
2. 여러 가지 방법의 다중 스레드 상황

public class ThreadTest {

  public static void main(String[] args) {
    Example example = new Example();

    Thread t1 = new Thread1(example);
    Thread t2 = new Thread2(example);
    t1.start();
    t2.start();
  }
}

class Example {
  public synchronized void excute() {
    for (int i = 0; i < 5; ++i) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("excute:" + i);
    }
  }
  public synchronized void excute1() {
    for (int i = 0; i < 5; ++i) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("excute1:" + i);
    }
  }

}

class Thread1 extends Thread {
  private Example example;

  public Thread1(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute();
  }
}

class Thread2 extends Thread {
  private Example example;

  public Thread2(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute1();
  }
}

실행 결과는 다음과 같습니다.
같은 순서로 실행합니다. 한 라인을 실행하고 다른 라인을 실행합니다.
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
synchronized 키워드를 제거하면 두 가지 방법이 동시에 실행되고 서로 영향을 주지 않습니다.
그러나 예제 프로그램에서 설명한 바와 같이 두 가지 방법이라도
실행 결과는 영원히 한 라인의 출력을 끝내고 다른 라인을 실행합니다.  
설명:
만약에 한 대상에 여러 개의synchronized 방법이 있다면, 어느 순간에 어떤 라인이 어떤synchronized 방법에 들어갔다면, 이 방법이 실행되기 전에 다른 라인은 이 대상의synchronized 방법에 접근할 수 없습니다.
결론:
synchronized 키워드가 하나의 방법을 수식할 때, 이 방법을 동기화 방법이라고 부른다.
자바의 모든 대상에는 자물쇠(lock) 또는 모니터(monitor)가 있습니다. 한 라인이 특정한 대상의synchronized 방법에 접근할 때 그 대상을 잠그고 다른 어떤 라인도 그 대상의synchronized 방법에 접근할 수 없습니다. (여기는 모든 동기화 방법을 가리키는 것이지 같은 방법이 아니라) 이전의 그 라인 실행 방법이 끝난 후(또는 이상을 던졌을 때까지)이 대상의 자물쇠를 풀어야 다른 라인이 대상의synchronized 방법에 접근할 수 있습니다.
이때 대상의 자물쇠를 잠그고 서로 다른 대상이라면 각 대상 사이에는 제한 관계가 없다.
코드에서 두 번째 루틴 대상을 구성할 때 새로운 Example 대상에 전송하려고 하면 두 루틴의 실행 사이에는 아무런 제약 관계가 없다.
3. 정적 동기화 방법
synchronized 키워드 수식 방법이 static에 의해 수식될 때 이전에 말했듯이 비정적 동기화 방법은 대상을 잠그지만 정적 방법은 대상이 아니라 클래스에 속한다. 이 방법이 있는 클래스의 클래스 대상을 잠그게 된다.

public class ThreadTest {

  public static void main(String[] args) {
    Example example = new Example();
    Example example2 = new Example();
    Thread t1 = new Thread1(example);
    Thread t2 = new Thread2(example2);
    t1.start();
    t2.start();
  }
}

class Example {
  public synchronized static void excute() {
    for (int i = 0; i < 5; ++i) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("excute:" + i);
    }
  }
  public synchronized static void excute1() {
    for (int i = 0; i < 5; ++i) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("excute1:" + i);
    }
  }

}

class Thread1 extends Thread {
  private Example example;

  public Thread1(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute();
  }
}

class Thread2 extends Thread {
  private Example example;

  public Thread2(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute1();
  }
}

실행 결과는 다음과 같습니다.
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
static 수식자가 없으면 두 라인이 각각 다른 대상에 전송되면 동시에 실행됩니다
따라서 정적 방법의 경우(execute ()와execute 2 () 모두static 키워드를 추가), 두 라인에 서로 다른 Example 대상을 전달하더라도 이 두 라인은 서로 제약이 있기 때문에 먼저 하나를 실행하고 다음을 실행해야 한다.
결론:
만약에 어떤synchronized 방법이static라면, 이 방법에 접근할 때, 이 방법은 synchronized 방법이 있는 대상이 아니라,synchronized 방법이 있는 클래스에 대응하는 Class 대상이다.Java에서 한 클래스에 몇 개의 대상이 있든지 간에 이 대상은 유일한 Class 대상에 대응하기 때문에 라인이 같은 클래스의 두 대상의static,synchronized 방법에 각각 접근할 때 그들의 실행 순서도 순서이다. 즉, 한 라인이 먼저 실행 방법에 가고 실행이 끝난 후에 다른 라인이 시작된다는 것이다.
4.synchronized 블록
synchronized(object)
{     
}
실행할 때object 대상을 잠그는 것을 나타냅니다.(이 대상은 임의의 종류의 대상일 수도 있고this 키워드를 사용할 수도 있음을 주의하십시오.)
이렇게 하면 스스로 잠금 대상을 규정할 수 있다.

public class ThreadTest {

  public static void main(String[] args) {
    Example example = new Example();
    Thread t1 = new Thread1(example);
    Thread t2 = new Thread2(example);
    t1.start();
    t2.start();
  }
}

class Example {
  public void excute() {
    synchronized (this) {
      for (int i = 0; i < 5; ++i) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("excute:" + i);
      }
    }
    
  }
  public void excute1() {
    synchronized (this) {
      for (int i = 0; i < 5; ++i) {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println("excute1:" + i);
      }
    }
    
  }

}

class Thread1 extends Thread {
  private Example example;

  public Thread1(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute();
  }
}

class Thread2 extends Thread {
  private Example example;

  public Thread2(Example example) {
    this.example = example;
  }

  @Override
  public void run() {
    example.excute1();
  }
}

실행 결과는 다음과 같습니다.
excute:0
excute:1
excute:2
excute:3
excute:4
excute1:0
excute1:1
excute1:2
excute1:3
excute1:4
예시 프로그램 4가 달성한 효과는 예시 프로그램 2의 효과와 마찬가지로 두 라인의 실행 순서를 병행하는 것이 아니라 한 라인이 실행될 때object 대상을 잠그고 다른 라인은 대응하는 블록을 실행할 수 없다.
synchronized 방법은 실제적으로synchronized 블록으로 방법의 모든 문장을 감싸고synchronized 블록의 괄호에this 키워드를 전달하는 것과 같다.물론 정적 방법이라면 잠금이 필요한 것은class 대상이다.  
한 방법에서 몇 줄의 코드만 라인 동기화 문제와 관련될 수 있기 때문에synchronized 블록은synchronized 방법보다 여러 라인의 접근을 세밀하게 제어하고synchronized 블록의 내용만 여러 라인에 동시에 접근할 수 없으며, 방법의 다른 문장은 여러 라인에 동시에 접근할 수 있다(synchronized 블록 이전과 이후를 포함한다).
결론:
synchronized 방법은 굵은 입도의 병발 제어로 어느 순간에 이 synchronized 방법을 실행하는 라인만 있을 수 있다.
synchronized 블록은 세립도의 병발 제어로 블록의 코드만 동기화할 수 있으며 방법 내,synchronized 블록 이외의 다른 코드는 여러 라인에 동시에 접근할 수 있다.
이상은 자바 다중 루틴 프로그래밍 Synchronized 블록 동기화 방법에 관한 것입니다. 여러분의 학습에 도움이 되기를 바랍니다.

좋은 웹페이지 즐겨찾기