• 线程间通信的几种方式


    1.通知等待模式

    等待方遵循如下原则。
    1. 获取对象的锁。
    2. 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件。
    3. 条件满足则执行对应的逻辑。
    对应的伪代码如下。
    synchronized(对象) {
      while(条件不满足) {
          对象.wait();
      }
      对应的处理逻辑
    }
    通知方遵循如下原则。
    1. 获得对象的锁
    2. 改变条件
    3. 通知所有等待在对象上的线程
    对应的伪代码如下。
    synchronized(对象) {
      改变条件
      对象.notifyAll();
    } 
    • 使用wait()、notify()和notifyAll()时需要先对调用对象加锁。
    • 调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列。
    • notify()或notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()或notifAll()的线程释放锁之后,等待线程才有机会从wait()返回。
    • notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。
    • 从wait()方法返回的前提是获得了调用对象的锁。

    2.管道

    管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。
    管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、
    PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。
    对于Piped类型的流,必须先要进行绑定,也就是调用connect()方法,如果没有将输入/输
    出流绑定起来,对于该流的访问将会抛出异常。

    3.Exchange

    Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方

    4.ThreadLocal

    ThreadLocal是如何为每个线程创建变量的副本的:
            首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本的值。
            初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
            然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
            总结一下:
            1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
            2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
            3)在进行get之前,必须先set,否则会报空指针异常;
            如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。

    ThreadLocal的应用场景

      最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。
           项目中的实际使用经验是在@Compont的多个方法中使用统一DBRecoud,为了避免重复查询,在第一次查询完以后保存到ThreadLocal,避免后续冗余查询.同时也避免了并发问题
      下面这段代码摘自:
    import java.sql.Connection;  
         import java.sql.DriverManager;  
         import java.sql.SQLException;  
           
         public class ConnectionManager {  
           
             private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {  
                 @Override  
                 protected Connection initialValue() {  
                     Connection conn = null;  
                     try {  
                         conn = DriverManager.getConnection(  
                                 "jdbc:mysql://localhost:3306/test", "username",  
                                 "password");  
                     } catch (SQLException e) {  
                         e.printStackTrace();  
                     }  
                     return conn;  
                 }  
             };  
           
             public static Connection getConnection() {  
                 return connectionHolder.get();  
             }  
           
             public static void setConnection(Connection conn) {  
                 connectionHolder.set(conn);  
             }  
         }  
  • 相关阅读:
    Java基础学习02--I/O字符流
    MacOS系统Web服务器
    git学习01--git基本命令
    dokcer学习02--Docker Compose基本使用
    dokcer学习01--docker安装(MacOS)与基本命令
    JVM学习01--JVM结构与代码存储位置
    奈氏准则和香农定理
    物理层接口特性、数据通信模型、物理层基本概念(数据、信号、码元 、信源、信道、信宿 、速率、波特、带宽)
    计算机网络第一章小结
    TCP/IP参考模型(应用层、传输层、网际层、网络接口层)、五层参考模型(应用层、传输层、网络层、数据链路层、物理层)、OSI与TCP/IP参考模型比较
  • 原文地址:https://www.cnblogs.com/wzj4858/p/8215223.html
Copyright © 2020-2023  润新知