当前位置: 首页 > 编程笔记 >

Java线程通信详解

白昊乾
2023-03-14
本文向大家介绍Java线程通信详解,包括了Java线程通信详解的使用技巧和注意事项,需要的朋友参考一下

线程通信用来保证线程协调运行,一般在做线程同步的时候才需要考虑线程通信的问题。

1、传统的线程通信

通常利用Objeclt类提供的三个方法:

  1. wait() 导致当前线程等待,并释放该同步监视器的锁定,直到其它线程调用该同步监视器的notify()或者notifyAll()方法唤醒线程。
  2. notify(),唤醒在此同步监视器上等待的线程,如果有多个会任意选择一个唤醒
  3. notifyAll() 唤醒在此同步监视器上等待的所有线程,这些线程通过调度竞争资源后,某个线程获取此同步监视器的锁,然后得以运行。

这三个方法必须由同步监视器对象调用,分为两张情况:

同步方法时,由于同步监视器为this对象,所以可以直接调用这三个方法。

示例如下:

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对象的await(), singal(),singalAll()分别对应wait(),notify()和notifyAll()方法。

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共有五个实现类:

ArrayBlockingQueue 基于数组实现的BlockingQueue队列

LinkedBlockingQueue 基于链表实现的BlockingQueue队列

PriorityBlockingQueue 中元素需实现Comparable接口,其中元素的排序是按照Comparator进行的定制排序。

SynchronousQueue 同步队列,要求对该队列的存取操作必须是交替进行。

DelayQueue 集合元素必须实现Delay接口,队列中元素排序按照Delay接口方法getDelay()的返回值进行排序。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 主要内容:1 什么是Java线程通信,2 线程间通信的过程分析,3 线程间通信的疑问,4 wait()和sleep()的区别,5 Java线程通信的例子1 什么是Java线程通信 线程间通信或协作就是允许同步线程彼此通信。 线程间通信是一种机制,其中一个线程在其关键部分中暂停运行,并允许另一个线程进入(或锁定)在同一关键部分中执行,这是通过以下Object类的方法实现的: wait() notify() notifyAll() 1.1 wait()方法 使当前线程释放锁定,并等待直到另一个线程为

  • 本文向大家介绍Java多线程中线程间的通信实例详解,包括了Java多线程中线程间的通信实例详解的使用技巧和注意事项,需要的朋友参考一下 Java多线程中线程间的通信 一、使用while方式来实现线程之间的通信 程序输出: 理解:线程Thread2不停地通过while语句检测这个条件(list.size()==5)是否成立 ,从而实现了线程间的通信。但是这种方式会浪费CPU资源。 二、wait no

  • 本文向大家介绍Java多线程通信实现方式详解,包括了Java多线程通信实现方式详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java多线程通信实现方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程通信的方式: 1、共享变量 线程间通信可以通过发送信号,发送信号的一个简单方式是在共享对象的变量里设置信号值。线程A在一个同

  • 问题内容: Java上下文中的线程和进程之间有什么区别?用Java如何实现进程间通信和线程间通信?请给我指出一些现实生活中的例子。 问题答案: 根本的区别是线程位于相同的地址空间中,而进程位于不同的地址空间中。这意味着线程间通信是关于传递对对象的引用以及更改共享对象,而进程是关于传递对象的序列化副本。 在实践中,Java线程间通信可以实现为对共享对象进行简单的Java方法调用,并引入适当的同步。或

  • 本文向大家介绍java 多线程-线程通信实例讲解,包括了java 多线程-线程通信实例讲解的使用技巧和注意事项,需要的朋友参考一下 线程通信的目标是使线程间能够互相发送信号。另一方面,线程通信使线程能够等待其他线程的信号。 通过共享对象通信 忙等待 wait(),notify()和 notifyAll() 丢失的信号 假唤醒 多线程等待相同信号 不要对常量字符串或全局对象调用 wait() 通过共

  • 问题内容: Java相互依赖的线程如何通信? 例如,我正在使用需要来自其他线程的数据的线程构建Web搜寻器。 问题答案: 这取决于通信的性质。 它是双工的吗(即A与B对话,B与A对话)? 是数据通信还是 完成 通信? 等等。 线程间通信的最简单,最可取的形式就是等待其他线程的完成。使用以下命令最容易做到: 在第一个任务完成之前,第二个任务将不会执行。 Java 5+具有 许多 并发实用程序来处理这