자바 스 레 드 간 공유 실현 방법 상세 설명

1.synchronize 대상 잠 금 및 클래스 잠 금
synchronize 는 다 중 스 레 드 키 워드 는 동기 화 자물쇠 로 다음 과 같은 몇 가지 대상 을 수식 할 수 있 습 니 다.
코드 블록:수 정 된 코드 블록 은 동기 코드 블록 이 라 고 불 리 며,역할 의 범 위 는{}안의 코드 이 며,역할 의 대상 은 이 코드 블록 을 호출 하 는 대상 입 니 다.
방법:수 정 된 방법 을 동기 화 방법 이 라 고 하 는데 작용 의 범 위 는 전체 방법 이 고 작용 의 대상 은 이 방법 을 호출 하 는 대상 이다.
클래스:역할 의 범 위 는 synchronize 뒤의 괄호 안의 부분 이 고 역할 의 대상 은 현재 클래스 입 니 다.
1.대상 잠 금
다음은 밤 하나 로 들 어 갑 니 다.

public class TestSynchronize {
  //        
  private synchronized void syn(){
    //   sleep   
    SleepTools.second(2);
    System.out.println("syn is going..."+this.toString());
    SleepTools.second(2);
    System.out.println("syn ended..."+this.toString());
  }

  //           1
  private static class thread implements Runnable{
    private TestSynchronize testSynchronize;
    public thread(TestSynchronize testSynchronize){
      this.testSynchronize = testSynchronize;
    }
    @Override
    public void run() {
      System.out.println("thread is running...");
      testSynchronize.syn();
    }
  }
  //           2
  private static class thread2 implements Runnable{
    private TestSynchronize testSynchronize;
    public thread2(TestSynchronize testSynchronize){
      this.testSynchronize = testSynchronize;
    }
    @Override
    public void run() {
      System.out.println("thread2 is running...");
      testSynchronize.syn();
    }
  }
  public static void main(String[] args) {
    TestSynchronize testSynchronize = new TestSynchronize();
    thread thread = new thread(testSynchronize);

    TestSynchronize testSynchronize2 = new TestSynchronize();
    thread2 thread2 = new thread2(testSynchronize);
    //thread2 thread2 = new thread2(testSynchronize2);
    new Thread(thread).start();
    new Thread(thread2).start();
  }
}

/**
       testSynchronize   (                  )      :
thread is running...
thread2 is running...
syn is going...com.zl.synchronize.TestSynchronize@6b52350c
syn ended...com.zl.synchronize.TestSynchronize@6b52350c
syn is going...com.zl.synchronize.TestSynchronize@6b52350c
syn ended...com.zl.synchronize.TestSynchronize@6b52350c
*/

/**
     testSynchronize,     testSynchronize2        :
thread is running...
thread2 is running...
syn is going...com.zl.synchronize.TestSynchronize@28835f5f
syn is going...com.zl.synchronize.TestSynchronize@47c48106
syn ended...com.zl.synchronize.TestSynchronize@28835f5f
syn ended...com.zl.synchronize.TestSynchronize@47c48106
*/
결론:여러 스 레 드 가 같은 대상 을 호출 하 는 동기 화 방법 이 막 히 고 서로 다른 대상 을 호출 하 는 동기 화 방법 이 막 히 지 않 습 니 다.
2.유형 자물쇠
1)synchronized 수식 의 정적 방법

public static synchronized void obj3() {
  int i = 5;
  while (i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
      Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
  }
}
2)synchronized(test.class),자물쇠 의 대상 은 test.class,즉 test 류 의 자물쇠 입 니 다.

public void obj1() {
  synchronized (test.class) {
    int i = 5;
    while (i-- > 0) {
      System.out.println(Thread.currentThread().getName() + " : " + i);
      try {
        Thread.sleep(500);
      } catch (InterruptedException ie) {
      }
    }
  }
}
그러면 문제 가 생 겼 습 니 다.한 가지 유형 에서 두 가지 방법 이 있 는데 각각 synchronized 로 수식 하 는 정적 방법(클래스 잠 금)과 비 정적 방법(대상 잠 금)이 있 습 니 다.다 중 스 레 드 가 두 가지 방법 에 접근 할 때 스 레 드 가 막 히 지 않 습 니까?
정 답 은 자물쇠 와 대상 자물쇠 가 동시에 존재 할 때 다 중 스 레 드 가 접근 할 때 막 히 지 않 는 다 는 것 이다.왜냐하면 그들 은 자물쇠 가 아니 기 때문이다.
2.휘발 성
volatile 은 유형의 수정자 입 니 다.volatile 의 역할 은 명령 키워드 로 서 이 명령 이 컴 파일 러 의 최적화 로 인해 생략 되 지 않도록 하 는 것 이다.
volatile 의 특성
4.567917.서로 다른 스 레 드 가 이 변 수 를 조작 할 때의 가시 성 을 확보 했다.즉,한 스 레 드 가 특정한 변수의 값 을 수정 했다.이 새 값 은 다른 스 레 드 에 있어 서 즉시 볼 수 있다.(가시 성 실현)
  • 명령 정렬 을 금지 합 니 다.(질서 성 실현)
  • 4.567917.volatile 은 한 번 의 읽 기/쓰기 에 대한 원자 성 만 보장 할 수 있다.i++이런 조작 은 원자 성 을 보장 할 수 없다3.ThreadLocal
    4.567917.ThreadLocal 은 글자 의 의미 에서 이해 할 수 있 고 스 레 드 로 컬 변수 이 며 스 레 드 로 컬 변수 라 고도 할 수 있 습 니 다.때때로 한 대상 의 변수 가 여러 스 레 드 에 접근 할 때 스 레 드 안전 문제 가 발생 할 수 있 습 니 다.물론 synchronized 키 워드 를 사용 하여 이 변 수 를 잠 글 수 있 습 니 다.동기 화 처 리 를 통 해 한 스 레 드 만 이 변 수 를 사용 할 수 있 도록 제한 할 수 있 습 니 다.그러나 이렇게 하면 프로그램의 실행 효율 에 영향 을 줄 수 있 습 니 다.이때 ThreadLocal 은 사용 할 수 있 습 니 다.
  • ThreadLocal 유지 변 수 를 사용 할 때 이 변 수 를 사용 하 는 모든 스 레 드 에 독립 된 변수 사본 을 제공 합 니 다.즉,모든 스 레 드 내부 에 현재 변수 가 있 습 니 다.이렇게 하면 여러 개의 스 레 드 가 이 변 수 를 방문 하 는 것 은 서로 영향 을 주지 않 습 니 다.그들 은 모두 각자 의 스 레 드 로 저 장 된 변수 이기 때문에 스 레 드 안전 에 문제 가 존재 하지 않 습 니 다.
  • 4.567917.동기 화 체 제 는'시간 으로 공간 을 바꾼다'는 방식 을 사 용 했 고 ThreadLocal 은'공간 으로 시간 을 바꾼다'는 방식 을 사용 했다.전 자 는 하나의 변수 만 제공 하여 서로 다른 스 레 드 를 줄 지어 방문 하 게 했 고 후 자 는 모든 스 레 드 에 변 수 를 제공 하기 때문에 동시에 방문 할 수 있 고 서로 영향 을 주지 않 았 다다음은 테스트 프로그램 을 드 립 니 다.
    
    public class ThreadLocalDemo {
      private static ThreadLocal<Integer> number = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
          return 1;
        }
      };
      private static class thread extends Thread{
        @Override
        public void run() {
          Integer number = ThreadLocalDemo.number.get();
          for (int i = 0; i < this.getId(); i++) {
            number++;
          }
          System.out.println(this.getName()+"---"+this.getId()+"===="+number);
        }
      }
      private static class thread2 extends Thread{
        @Override
        public void run() {
          Integer number = ThreadLocalDemo.number.get();
          for (int i = 0; i < this.getId(); i++) {
            number++;
          }
          System.out.println(this.getName()+"---"+this.getId()+"===="+number);
        }
      }
      public static void main(String[] args) {
        new Thread(new thread()).start();
        new Thread(new thread2()).start();
      }
    }
    /**
    Thread-0---12====13
    Thread-2---14====15
    */
    4.대기(Wait)와 알림(notify)
    다 중 스 레 드 간 의 협력 을 지탱 하기 위해 JDK 는 매우 중요 한 스 레 드 인 터 페 이 스 를 제공 합 니 다.wait()방법 과 알림 notify()방법 을 기다 리 는 것 입 니 다.이 두 가지 방법 은 Thread 클래스 가 아니 라 Object 클래스 에 출력 되 는 것 입 니 다.이것 은 모든 대상 이 이 두 가지 방법 을 호출 할 수 있다 는 것 을 의미한다.
    대기/알림 의 고전 패 러 다 임

    wait()방법 과 notify()방법 은 도대체 어떻게 일 합 니까?
    하나의 스 레 드 가 object.wait()방법 을 호출 하면 object 대상 의 대기 열 에 들 어 갑 니 다.이 대기 열 에는 여러 스 레 드 가 있 을 수 있 습 니 다.시스템 이 여러 스 레 드 를 실행 하 는 동시에 대상 을 기다 리 기 때 문 입 니 다.
    object.notify()방법 이 호출 될 때 이 대기 열 에서 무 작위 로 스 레 드 를 선택 하고 깨 웁 니 다.
    notity()방법 을 제외 하고 Object 대상 은 notify All()과 유사 한 방법 이 있 습 니 다.notity 방법의 기능 과 대체적으로 일치 합 니 다.다른 것 은 이 대기 열 에서 기다 리 는 모든 스 레 드 를 무 작위 로 깨 우 는 것 이 아 닙 니 다.
    object.wait()방법 은 함부로 호출 할 수 없습니다.대상 의 synchronzied 구문 에 포함 되 어야 합 니 다.wait()방법 이나 notity()방법 이 든 먼저 대상 의 모니터 를 얻어 야 합 니 다.
    T1 과 T2 는 두 개의 스 레 드 를 표시 합 니 다.T1 은 wait()방법 을 정확하게 실행 하기 전에 object 대상 의 모니터 를 받 아야 합 니 다.wait()방법 이 실 행 된 후에 이 모니터 를 방출 합 니 다.
    이렇게 하 는 목적 은 다른 object 대상 에서 기다 리 는 스 레 드 가 T1 의 휴면 으로 인해 모두 정상적으로 실행 되 지 않도록 하 는 것 이다.
    스 레 드 T2 는 notity()방법 이 호출 되 기 전에 object 대상 의 모니터 를 가 져 와 야 합 니 다.이때 T1 은 이미 이 모니터 를 풀 었 다.그래서 T2 는 object 대상 의 모니터 를 순조롭게 얻 을 수 있 습 니 다.
    이 어 T2 는 notify()방법 을 실행 하여 대기 스 레 드 를 깨 우려 고 시 도 했 습 니 다.여기 서 T1 을 깨 웠 다 고 가정 합 니 다.T1 이 깨 어 난 후에 해 야 할 첫 번 째 일 은 후속 코드 를 실행 하 는 것 이 아니 라 다시 시도 하 는 것 입 니 다.
    object 대상 의 모니터 를 가 져 옵 니 다.이 모니터 는 T1 이 wait()방법 을 실행 하기 전에 가지 고 있 는 것 입 니 다.
    잠시 얻 을 수 없다 면 T1 은 이 모니터 를 기 다 려 야 합 니 다.모니터 가 순조롭게 획득 되 어야 T1 은 진정한 의미 에서 계속 실 행 될 수 있다.
    wait()와 notify()에 포 함 된 synchronized 문 구 를 실행 해 야 모니터 를 풀 수 있 습 니 다.
    이해 하기 편리 하고 간단 한 사례:
    
    public class testWaitAndNotify {
      final static Object object = new Object();
    
      public static class T1 extends Thread {
        public void run() {
          synchronized (object) {
            try {
              System.out.println(System.currentTimeMillis() + ":T1 start! ");
              System.out.println(System.currentTimeMillis() + ":T1 wait for object");
              object.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
            System.out.println(System.currentTimeMillis() + ":T1 end! ");
          }
        }
      }
    
      public static class T2 extends Thread {
        public void run() {
          synchronized (object) {
            try {
              System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread");
              object.notify();
              sleep(5000);
              System.out.println(System.currentTimeMillis() + ":T2 end! ");
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }
      }
    
      public static void main(String[] args) {
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
      }
    }
    /**
    1571039516250:T1 start! 
    1571039516250:T1 wait for object
    1571039516251:T2 start! notify one thread
    1571039521251:T2 end! 
    1571039521251:T1 end! 
    */
    5.대기 시간 초과 모드
    전형 적 인 대기/알림 패 러 다 임 으로 인해 시간 을 초과 하여 기다 릴 수 없다.즉,소비자 가 자 물 쇠 를 얻 은 후에 조건 이 만족 하지 않 으 면 생산자 가 조건 을 바 꾸 기 전에 대기 상태 에 있 고 일부 실제 응용 에서 자원 을 낭비 하여 운행 효율 을 낮 출 수 있다.
    의사 코드 는 다음 과 같다.
    
    //       mills,        remaining,     future
    long future = System.currentTimeMillis() + mills;
    long remaining = mills;
    synchronized (lock) {
      while (!condition && remaining > 0) {
        wait(remaining);
        remaining = future - System.currentTimeMillis();
      }
      //    
    }
    6.join()
    join 은 스 레 드 에서'새치기'를 의미 합 니 다.어떤 스 레 드 가 join 을 대표 하 는 스 레 드 새치기 가 먼저 실 행 됩 니까?그러나 누구의 팀 을 삽입 하 는 지 에 대해 신경 을 썼 습 니 다.팀 에 끼어 서 게 를 먹 는 첫 번 째 사람 이 될 수 있 는 것 이 아니 라 현재 운행 스 레 드 앞 에 꽂 았 습 니 다.예 를 들 어 시스템 이 현재 운행 스 레 드 A,온라인 스 레 드 A 에서 스 레 드 B.join 방법 을 호출 했 습 니 다.그 다음 에 스 레 드 B 는 스 레 드 A 앞에서 먼저 실행 하고 스 레 드 B 가 모두 실 행 된 후에 야 스 레 드 A 를 계속 실행 할 것 이다.
    말 이 많 지 않 으 면 코드 를 말 해라.
    
    public class TestJoin {
      private static class thread extends Thread{
        private Thread t;
        //        
        public thread(Thread t){
          this.t = t;
        }
        @Override
        public void run() {
          try {
            //       join  
            t.join();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(getName()+"---    !");
        }
      }
      public static void main(String[] args) throws InterruptedException {
        //             
        Thread pre = Thread.currentThread();
        //      
        for (int i = 0; i < 5; i++) {
          Thread thread = new Thread(new thread(pre),String.valueOf(i));
          //    
          thread.start();
          //       
          pre = thread;
        }
        System.out.println(System.currentTimeMillis());
        //      2s
        Thread.currentThread().sleep(2000);
        System.out.println(System.currentTimeMillis());
        System.out.println(Thread.currentThread().getName()+"---    ");
      }
    }
    
    /**
    1571061168064
    1571061170065
    main---    
    Thread-0---    !
    Thread-1---    !
    Thread-2---    !
    Thread-3---    !
    Thread-4---    !
    */
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기