자바 라인과synchronized 키워드를 깊이 있게 설명하다

우리는 다음과 같은 몇 가지 점에서 자바 라인의 일부 개념을 이해할 것이다.
  • 라인의 기본 개념과 우열점
  • 하나의 라인을 만드는 두 가지 방식
  • 라인의 속성
  • 라인의 상태
  • synchronized 수식 가능한 방법
  • synchronized의 중요한 특성
  • 1. 라인의 기본 개념
    컴퓨터에는 프로세스와 루틴이라는 두 가지 개념이 있는데 프로세스에 여러 개의 루틴이 있을 수 있다. 이것은 종속 관계이다. 프로세스는 종종 자원의 점유자 같다. 루틴이야말로 프로그램의 집행자이고 여러 루틴 간에 프로세스 중의 자원을 공유하고 있다.하나의 cpu는 동시에 하나의 라인만 실행할 수 있으며, 모든 라인은 하나의 타임 슬라이스가 있으며, 타임 슬라이스가 다 사용하면 막히고 CPU의 제어권을 양보하여 다음 라인에 사용합니다.이렇게 하면 컴퓨터에서 다중 작업의 가상을 실현할 수 있다. 사실 CPU는 여러 작업이 동시에 실행되는 것처럼 끊임없이 라인을 전환하고 있다.
    루틴을 사용하는 장점은 의심할 여지가 없다. CPU의 실행 효율을 높일 수 있다. 어떤 루틴이 특정한 자원(예를 들어 프린터를 기다리는 것)을 기다려야 한다면 CPU를 막아서 다른 루틴을 실행하게 할 수 있다. CPU가 이 루틴과 함께 특정한 자원을 기다리게 하지 않아도 시스템 효율을 높일 수 있다. 또 다른 하나는 이런 가상으로 사용자 체험도를 높일 수 있다는 것이다.그러나 CPU가 서로 다른 라인을 전환하는 데 드는 대가도 무시할 수 없다. 비교적 복잡한 프로그램에서 이런 열세는 구류일모일 수 있지만 간단한 프로그램에서 특히 두드러진다.
    2. 라인 만들기
    다음은 자바에서 여러 개의 라인을 동시에 실행할 수 있는 라인을 만드는 방법을 봅시다.첫 번째 방식은 자바에 클래스Thread가 있습니다. 우리는 이 클래스를 계승하고 그의 run 방법을 다시 쓰기만 하면 start 방법을 호출하면 새로운 라인을 시작할 수 있습니다.(못 본 학생이 다음 코드를 이해하지 못할 수도 있으니 아래에서 설명할게요)
    
     /* */
    public class Test_thread extends Thread {
     // Thread run 
     public void run(){
     System.out.println("i am the thread");
     }
    }
    
    public class Test_Class {
     public static void main(String[] args){
     Test_thread thread = new Test_thread();
     thread.start();
     }
    }
    출력 결과: i am the thread
    우선, 하나의 라인이 생성된 후에, 어떻게 실행을 시작할 수 있는지, 우리는thread를 호출합니다.start();방법은 하나의 라인을 시작하면 먼저 우리가 다시 쓰는 런 방법을 실행할 것이다. (다시 쓰지 않으면Thread류의run방법을 호출하고 아무것도 하지 않는다. 이것도 우리가 런 방법을 다시 쓰는 원인이다.) 즉, 런 방법은 하나의 라인의 시작이다.왜 start 방법을 사용했는데run 방법을 실행했는지 의문이 있습니다.사실 start 호출 방법은 라인의 시작을 위해 준비 작업을 하고 라인의 개인 창고 자원을 분배한 다음run 방법을 실행하는 것이다.
    다음은 인터페이스 Runnable를 실현하고 그 중의 run 방법을 다시 쓰는 두 번째 방식을 보여 줍니다.
    
     public class Test_thread implements Runnable {
     public void run(){
      System.out.println("i am the thread");
     }
    }
    
    public class Test_Class {
     public static void main(String[] args){
      Test_thread thread = new Test_thread();
      thread.start();// 
     }
    }
    run 방법을 다시 썼지만 start 방법을 호출할 때 컴파일이 잘못되었음을 발견할 수 있습니다. Runnable 인터페이스의 원본 코드에 들어가면 추상적인 방법인 run만 있기 때문에 라인을 시작하는 start 방법이 없기 때문에 Tread 클래스를 빌려야 합니다.
    
     public class Test_Class {
     public static void main(String[] args){
      Thread thread = new Thread(new Test_thread());
      thread.start();
     }
    }
    Thread 클래스에 start 방법이 있기 때문에Thread의 구조 함수를 사용하여 Runnable 인터페이스를 실현하는 유형으로 전송하여Thread 클래스를 구축할 수 있습니다.
    3. 라인의 속성과 상태
    다중 루틴 프로그램에서 우리는 루틴의 일부 속성을 사용하여 그것들을 구별하고 식별한다.
    
    private long tid;
    private volatile char name[];
    public static native Thread currentThread()
    private boolean  daemon = false;
    private volatile int threadStatus = 0;
    public final static int NORM_PRIORITY = 5;
    모든 스레드에는 이 스레드의 모든 스레드에 대한 정렬이 지정됩니다. 이 값은 점차적으로 증가합니다. 또한name은 이 스레드의 이름을 지정합니다. 또한 setName을 사용하여 우아한 스레드 이름을 설정할 수 있습니다. Thread 클래스는 현재 CPU를 사용하고 있는 스레드 대상을 되돌려주는 방법을 제공합니다.모든 스레드는 어느 순간에 상태가 있습니다. 속성threadStatus는 현재 대상의 스레드 상태가 무엇인지 기록합니다. 기본적으로 모든 스레드의 우선순위는 5로 설정됩니다. 특별한 수요가 있으면 스레드의 우선순위를 수정하여 어떤 스레드가 우선적으로 실행될 수 있도록 합니다.다음은 라인의 몇 가지 다른 상태를 소개한다.
  • New: 스레드가 생성되었습니다. 아직 시작되지 않았습니다
  • Runnable: 라인은 실행 가능한 상태이지만 반드시 실행 중인 상태는 아니다(뒤에 설명된 차이)
  • Blocked: 라인이 막힌 상태이고 일반적으로 어떤 자원이 성공하지 못하면 CPU를 양보하여 자신을 막는다(구체적인 차이점은 뒤에 말한다)
  • Waiting: 라인은 대기 상태이고 보통 대기 서비스입니다
  • Terminated: 라인이 종료됩니다. 즉, 라인이kill에 의해 점용된 자원을 방출합니다
  • 다음은 서로 다른 상태에서의 라인에 대한 몇 가지 조작을 구체적으로 말씀드리겠습니다. 우선 new를 보십시오. 라인은 이때부터 생성됩니다. 단지 운행 상태에서 약간의 준비가 필요합니다.Runnable는 스레드가 실행 가능한지 여부를 나타냅니다. 이미 실행 상태인지는 시스템이 준 타임슬라이스가 다 사용되었는지에 따라 달라집니다. 다 사용하면 이 스레드를 실행 가능한 스레드 대기열의 끝에 놓고 다음 분배 타임슬라이스가 다 사용되지 않으면 실행 상태입니다. (이것도 런닝이 아닌 Runnable라고 하는 이유입니다.)다음 두 가지 상태 Blocked와waiting은 모두 스레드가 막혀 CPU를 양보해야 한다는 것을 나타낸다.다만 이런 상태에 빠지게 된 원인은 다르다. 구체적으로는 우리가 synchronized 키워드를 소개한 후에 말하지 않아도 알 수 있다.
    4. 키워드synchronized
    먼저 코드 한 토막을 보십시오.
    
     public class Test_thread extends Thread{
     public static int count = 0;
     public void run(){
      try {
       Thread.sleep((int) (Math.random() * 100));
      }catch(InterruptedException e){
    
      }
      count++;
     }
    }
    
    public class Test_Class {
     public static void main(String[] args){
      Test_thread[] thread = new Test_thread[1000];
      for(int a=0;a<1000;a++){
       thread[a] = new Test_thread();
       thread[a].start();
      }
    
      for(int a=0;a<1000;a++){
       try {
        thread[a].join();
       }catch (InterruptedException e){
    
       }
      }
      System.out.println(Test_thread.count);
     }
    }
    직감에 따라 1000개의 라인을 만들고 각 라인은 랜덤으로 잠을 자고 공공 변수를 1씩 증가시킵니다.main 라인은 모든 라인의 실행이 끝난 후에 공공 변수의 값을 출력합니다.직감에 따라 답은 1000이어야 하지만 우리가 운행한 결과는 매번 다르다. 1000에 가깝지만 매번 다르다.왜 이러지?이것은 사실 간단하게 높은 병발을 시뮬레이션한 것이다. 몇 개의 라인이 같은 시간에 잤을 수도 있고, 동시에 깨어나서 얻은count값이 같을 수도 있다. 이로 인해 이 몇 개의 라인이count에 대한 조작이 덮어씌워진다.
    
     public class Test_thread extends Thread{
     public static int count = 0;
     public void run(){
      try {
       Thread.sleep((int) (Math.random() * 100));
      }catch(InterruptedException e){
    
      }
      /* */
      synchronized (Test_thread.class){
       count++;
      }
     }
    }
    간단한 키워드 하나로 이런 높은 병행 문제를 쉽게 해결할 수 있다.왜 이렇게 썼지?synchronized 키워드를 소개한 후에 틀림없이 너는 알게 될 것이다.
    우선 우리는 모든 대상에 내부 자물쇠가 있다는 것을 알아야 한다.그래서synchronized 키워드에 의해 수식되는 방법은 사실 내부 대상 자물쇠를 추가한 것이다.우리는 코드를 보았다.
    
      public class Counter{
      private int count;
    
      /* */
      public synchronized int getCount(){
       return count;
      }
     }
    실례적인 방법에 키워드를 추가하는 것은 사실상 현재의 대상에 자물쇠를 채우는 것이다.괄호 안에 잠글 객체가 있습니다.
    
      public class Counter{
      private int count;
    
      /* */
      synchronized(this){
       return count;
      }
     }
    정적 방법에 대해 실제로는 이런 종류에 자물쇠를 넣는다.
    
      public class Counter{
      private static int count;
    
      public static synchronized int getCount(){
       return count;
      }
     }
    
    /* */
     public class Counter{
      private int count;
    
      synchronized(Counter.class){
       return count;
      }
     }
    5. synchronized의 일부 특성을 깊이 이해한다
    첫 번째 성질은 중입성이다.synchronized로 수식된 방법의 모든 조작은 원자 조작이지만, 우리가 그 중에서 다른 자물쇠가 필요한 코드에 접근해야 할 때 다른 자물쇠를 직접 얻을 수 있습니다.즉, 현재의 대상은 여러 개의 자물쇠를 얻을 수 있다.
    두 번째 성질은 메모리의 가시성이다.우리 컴퓨터에서 사실 많은 조작이 결코 시원스럽지 않다. 예를 들어 당신이 데이터베이스에 데이터를 저장할 때 사실 저장해야 할 데이터가 캐시에 축적되어 일정한 데이터 양을 기다릴 때 통일적으로 데이터베이스에 저장될 때가 많다. 그러나 우리가 보기에 이런 데이터는 사실 데이터베이스에 이미 저장되었어야 한다.이것은 메모리의 보이지 않는 문제를 초래했다.물론 우리도 키워드synchronized를 사용하여 매번 추출한 데이터가 최신임을 보장할 수 있다.자물쇠를 놓을 때 바로 데이터 협회 메모리를 저장하고 자물쇠를 얻을 때 최신 내용을 읽습니다.
    
      public class Counter{
      private int count;
    
      public synchronized int getCount(){
       return count;
      }
     }
     /* , count */
    사실, 메모리의 가시성을 확보하기 위해서만synchronized 키워드를 사용하면 대가가 좀 높을 수 있습니다. 이런 상황에서 키워드volatile를 사용할 수 있습니다.
    
      public class Counter{
      /* volatile*/
      private volatile int count;
    
      public int getCount(){
       return count;
      }
     }
     /* count */
    총결산
    이상이 바로 이 문장의 전체 내용입니다. 아니면 여러분이 그 중의 잘못을 발견하고 직접 지적하여 제가 잘못을 바로잡고 지식을 갱신하는 데 편리하기를 바랍니다.자바 병발 시리즈, 많은 관심 부탁드립니다.

    좋은 웹페이지 즐겨찾기