线程间通信的几种方式

 

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);  
         }  
     }  
posted @ 2018-01-06 20:33  daniel456  阅读(2181)  评论(0编辑  收藏  举报