使用Exchanger实现两个线程之间的数据交互

简介:
在看Jetty源码中的EndPointTest类,对EndPoint的测试,我的思路是:
1. 建立一个连接(创建ServerSocket实例,一般还会给定一个端口,其实可以bind(null)以让操作系统分配一个可用端口),新启动一个线程,在新线程中监听给定端口(调用accept方法)。
2. 发送客户端请求(创建一个Socket实例,并向该Socket写入请求数据)。
3. 在接收端读取数据,验证写入的请求和接收到的数据相同。

在以上流程实现中,accept方法返回的接收端Socket需要传给主线程,同时要保证使用该Socket是在accept方法返回之后,以我习惯,我会使用一个Lock或CountDownLatch:
private  static  class SocketHolder {
    Socket socket;
}

@Test
public  void levinOldWayTest()  throws Exception {
     final ServerSocket server =  new ServerSocket(10240);
    
     final CountDownLatch latch =  new CountDownLatch(1);
     final SocketHolder socketHolder =  new SocketHolder();
     new Thread() {
         public  void run() {
             try {
                socketHolder.socket = server.accept();
                latch.countDown();
            }  catch(Exception ex) {
                ex.printStackTrace();
            }
        }
    }.start();
    
    Socket socket =  new Socket(server.getInetAddress(), server.getLocalPort());
    socket.getOutputStream().write("My Test String".getBytes());
    
    latch.await(5, TimeUnit.SECONDS);
     byte[] receives =  new  byte[4096];
     int length = socketHolder.socket.getInputStream().read(receives);
    
    assertEquals("My Test String",  new String(receives, 0, length));
    
    socket.close();
    socketHolder.socket.close();
    server.close();
}

不知道有多少人也像我一样把这段代码写成这样?这里有两个问题:
1. ServerSocket的监听的端口不一定是可用的,类似测试代码我之前没有写过,我估计自己正真在写的时候应该会想到让操作系统动态分配。
2. 为了在两个线程中传递数据,这里首先创建了一个SocketHolder类,然后使用CountDownLatch,写起来好麻烦。为了简化这段代码,可以使用Exchanger,即当一个生产者线程准备好数据后可以通过Exchanger将数据传递给消费者,而消费者在生产者传递过来数据后就可以消费了,这里的数据就是Socket。

改进后的代码如下:
@Test
public  void levinImprovedWayTest()  throws Exception {
     final ServerSocket server =  new ServerSocket();
    server.bind( null);
    
     final Exchanger<Socket> exchanger =  new Exchanger<Socket>();
     new Thread() {
         public  void run() {
             try {
                exchanger.exchange(server.accept());
            }  catch(Exception ex) {
                ex.printStackTrace();
            }
        }
    }.start();
    
    Socket socket =  new Socket(server.getInetAddress(), server.getLocalPort());
    socket.getOutputStream().write("My Test String".getBytes());
    
    Socket receiverSocket = exchanger.exchange( null, 5, TimeUnit.SECONDS);
     byte[] receives =  new  byte[4096];
     int length = receiverSocket.getInputStream().read(receives);
    
    assertEquals("My Test String",  new String(receives, 0, length));
    
    socket.close();
    receiverSocket.close();
    server.close();
}

相关文章
|
28天前
|
存储 前端开发 Java
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
【C++ 多线程 】C++并发编程:精细控制数据打印顺序的策略
43 1
|
3月前
|
存储 Java 数据安全/隐私保护
【JUC】ThreadLocal 如何实现数据的线程隔离?
【1月更文挑战第15天】【JUC】ThreadLocal 如何实现数据的线程隔离?ThreadLocal 导致内存泄漏问题?
|
6月前
|
安全 Java 容器
Java多线程编程中的线程安全集合:保护数据的铁壁
Java多线程编程中的线程安全集合:保护数据的铁壁
85 1
|
7月前
|
Arthas IDE Java
一种获取阻塞线程栈帧数据的思路
一种获取阻塞线程栈帧数据的思路
125 1
|
1月前
|
Java easyexcel 应用服务中间件
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
【二十五】springboot使用EasyExcel和线程池实现多线程导入Excel数据
161 0
|
3月前
|
存储 缓存 安全
《C++ Concurrencyin Action》第3章--线程间共享数据
《C++ Concurrencyin Action》第3章--线程间共享数据
|
3月前
|
缓存
Long包装类型的享元模式注意事项
昨天修复订单接口的bug
30 0
|
4月前
|
网络协议 NoSQL Linux
知识巩固源码落实之5:http get异步请求数据demo(多线程+struct epoll_event的ptr)
知识巩固源码落实之5:http get异步请求数据demo(多线程+struct epoll_event的ptr)
25 0
|
4月前
|
安全 Java
Java【代码分享 09】多线程处理List数据核心代码说明(下标越界、数据丢失及效率问题)
Java【代码分享 09】多线程处理List数据核心代码说明(下标越界、数据丢失及效率问题)
40 0
|
5月前
|
Linux
linux线程私有数据详解
linux线程私有数据详解
44 0