>  기사  >  Java  >  Java 스레드 통신에 대한 자세한 설명

Java 스레드 통신에 대한 자세한 설명

高洛峰
高洛峰원래의
2017-01-05 15:20:481557검색

스레드 통신은 스레드의 조화로운 작업을 보장하기 위해 사용됩니다. 일반적으로 스레드 동기화를 수행할 때 스레드 통신 문제를 고려해야 합니다.

1. 기존 스레드 통신

은 일반적으로 Objeclt 클래스에서 제공하는 세 가지 메서드를 사용합니다.

wait()는 현재 스레드를 기다리게 하고 동기화 잠금을 해제합니다. monitor 는 다른 스레드가 동기화 모니터의 inform() 또는 informAll() 메서드를 호출하여 스레드를 깨울 때까지 유지됩니다.

notify(), 이 동기화 모니터에서 대기 중인 스레드를 깨웁니다. 여러 개가 있는 경우 하나를 무작위로 선택하여 깨우게 됩니다.

notifyAll(), 이 동기화 모니터에서 대기 중인 모든 스레드를 깨웁니다. 동기화 모니터 - 이러한 스레드가 예약을 통해 리소스를 놓고 경쟁한 후 스레드는 이 동기화 모니터의 잠금을 획득한 다음 실행됩니다.

이 세 가지 메소드는 동기화 모니터 객체에 의해 호출되어야 하며 두 가지 상황으로 나누어집니다.

메서드 동기화 시 동기화 모니터가 이 객체이므로 이 세 가지 메소드를 직접 호출할 수 있습니다. .

예는 다음과 같습니다.

public class SyncMethodThreadCommunication {
  static class DataWrap{
    int data = 0;
    boolean flag = false;
     
    public synchronized void addThreadA(){
      if (flag) {
        try {
          wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      } 
       
      data++;
      System.out.println(Thread.currentThread().getName() + " " + data);
      flag = true;
      notify();
    }
     
    public synchronized void addThreadB() {
      if (!flag) {
        try {
          wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      } 
       
      data++;
      System.out.println(Thread.currentThread().getName() + " " + data);
      flag = false;
      notify();
    }
  }
   
  static class ThreadA extends Thread {
    private DataWrap data;
     
    public ThreadA(DataWrap dataWrap) {
      this.data = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        data.addThreadA();
      }
    }
  }
   
  static class ThreadB extends Thread {
    private DataWrap data;
     
    public ThreadB(DataWrap dataWrap) {
      this.data = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        data.addThreadB();
      }
    }
  }
   
  public static void main(String[] args) {
    //实现两个线程轮流对数据进行加一操作
    DataWrap dataWrap = new DataWrap();
     
    new ThreadA(dataWrap).start();
    new ThreadB(dataWrap).start();
  }
 
}

코드 블록을 동기화할 때 모니터 개체를 사용하여 이 세 가지 메서드를 호출해야 합니다.

예제는 다음과 같습니다.

public class SyncBlockThreadComminication {
  static class DataWrap{
    boolean flag;
    int data;
  }
   
  static class ThreadA extends Thread{
    DataWrap dataWrap;
     
    public ThreadA(DataWrap dataWrap){
      this.dataWrap = dataWrap;
    }
     
    @Override
    public void run() {
      for(int i = 0 ; i < 10; i++) {
        synchronized (dataWrap) {
          if (dataWrap.flag) {
            try {
              dataWrap.wait();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
           
          dataWrap.data++;
          System.out.println(getName() + " " + dataWrap.data);
          dataWrap.flag = true;
          dataWrap.notify();
        }  
      }
    }
  }
   
  static class ThreadB extends Thread{
    DataWrap dataWrap;
     
    public ThreadB(DataWrap dataWrap){
      this.dataWrap = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
          synchronized (dataWrap) {
            if (!dataWrap.flag) {
              try {
                dataWrap.wait();
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
             
            dataWrap.data++;
            System.out.println(getName() + " " + dataWrap.data);
            dataWrap.flag = false;
            dataWrap.notify();
          }
        }  
      }
       
  }
  public static void main(String[] args) {
    //实现两个线程轮流对数据进行加一操作
     
    DataWrap dataWrap = new DataWrap();
    new ThreadA(dataWrap).start();
    new ThreadB(dataWrap).start();
  }
 
}

2. Condition을 사용하여 스레드 통신을 제어합니다.

동기화를 보장하기 위해 Lock 개체를 사용할 경우 조정을 보장하기 위해 Condition 개체를 사용합니다.

예제는 다음과 같습니다.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
import com.sun.media.sound.RIFFInvalidDataException;
 
import javafx.scene.chart.PieChart.Data;
 
public class SyncLockThreadCommunication {
  static class DataWrap {
    int data;
    boolean flag;
     
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
     
    public void addThreadA() {
      lock.lock();
      try {
        if (flag) {
          try {
            condition.await();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
         
        data++;
        System.out.println(Thread.currentThread().getName() + " " + data);
        flag = true;
        condition.signal();
      } finally {
        lock.unlock();
      }
    }
     
    public void addThreadB() {
      lock.lock();
      try {
        if (!flag) {
          try {
            condition.await();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
         
        data++;
        System.out.println(Thread.currentThread().getName() + " " + data);
        flag = false;
        condition.signal();
      } finally {
        lock.unlock();
      }
    }
  }
   
  static class ThreadA extends Thread{
    DataWrap dataWrap;
     
    public ThreadA(DataWrap dataWrap) {
      this.dataWrap = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        dataWrap.addThreadA();
      }
    }
  }
   
  static class ThreadB extends Thread{
    DataWrap dataWrap;
     
    public ThreadB(DataWrap dataWrap) {
      this.dataWrap = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 10; i++) {
        dataWrap.addThreadB();
      }
    }
  }
   
  public static void main(String[] args) {
    //实现两个线程轮流对数据进行加一操作
     
    DataWrap dataWrap = new DataWrap();
    new ThreadA(dataWrap).start();
    new ThreadB(dataWrap).start();
  }
 
}

Condition 객체의 wait(), singal(), singalAll()은 wait(), inform(), and에 해당합니다. 각각 informAll() 메서드를 사용합니다.

3. BlockingQueue를 사용하여 스레드 통신 제어

BlockingQueue는 Queue 인터페이스의 하위 인터페이스로 주로 스레드 통신에 사용됩니다. BlockingQueue에 들어가려고 시도합니다. 요소를 넣을 때 대기열이 가득 차면 스레드가 차단됩니다. 소비자 스레드가 BlockingQueue에서 요소를 가져오려고 하면 대기열이 비어 있으면 스레드가 차단됩니다. 이 두 기능은 각각 차단을 지원하는 두 가지 메서드인 put(E e) 및 take()에 해당합니다

예는 다음과 같습니다.

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
 
public class BlockingQueueThreadComminication {
  static class DataWrap{
    int data;
  }
   
  static class ThreadA extends Thread{
    private BlockingQueue<DataWrap> blockingQueue;
     
    public ThreadA(BlockingQueue<DataWrap> blockingQueue, String name) {
      super(name);
      this.blockingQueue = blockingQueue;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 100; i++) {
        try {
          DataWrap dataWrap = blockingQueue.take();
           
          dataWrap.data++;
          System.out.println(getName() + " " + dataWrap.data);
          sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
   
  static class ThreadB extends Thread{
    private BlockingQueue<DataWrap> blockingQueue;
    private DataWrap dataWrap;
     
    public ThreadB(BlockingQueue<DataWrap> blockingQueue, DataWrap dataWrap, String name) {
      super(name);
      this.blockingQueue = blockingQueue;
      this.dataWrap = dataWrap;
    }
     
    @Override
    public void run() {
      for (int i = 0; i < 100; i++) {
        try {
          dataWrap.data++;
          System.out.println(getName() + " " + dataWrap.data);
          blockingQueue.put(dataWrap);
          sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
   
  public static void main(String[] args) {
    ///实现两个线程轮流对数据进行加一操作
     
    DataWrap dataWrap = new DataWrap();
    BlockingQueue<DataWrap> blockingQueue = new ArrayBlockingQueue<>(1);
     
    new ThreadA(blockingQueue, "Consumer").start();
    new ThreadB(blockingQueue, dataWrap, "Producer").start();
  }
 
}

BlockingQueue에는 5개의 구현 클래스가 있습니다.

ArrayBlockingQueue 배열 기반으로 구현된 BlockingQueue 큐

LinkedBlockingQueue 연결 목록 기반으로 구현된 BlockingQueue 큐

PriorityBlockingQueue의 요소는 Comparable 인터페이스를 구현해야 하며 요소 정렬은 사용자 정의됩니다. 비교기에 따르면.

SynchronousQueue는 동기식 대기열이므로 대기열에 대한 액세스 작업을 교대로 수행해야 합니다.

DelayQueue 컬렉션 요소는 Delay 인터페이스를 구현해야 합니다. 대기열의 요소는 Delay 인터페이스 메서드 getDelay()의 반환 값에 따라 정렬됩니다.

위 내용은 이 글의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다.

자바 스레드 통신에 대한 더 자세한 글은 PHP 중국어 홈페이지를 참고해주세요!


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.