如何为线程池服务器预分配对象?

Posted

技术标签:

【中文标题】如何为线程池服务器预分配对象?【英文标题】:How to preallocate objects for thread pooled server? 【发布时间】:2016-11-18 12:09:33 【问题描述】:

在a simple test case 中,我实现了一个线程池服务器,在端口 12345 接受多达 10 个同时传入的 TLS PSK 连接,并在标准输出中打印解密数据:

public static void main(String[] args) throws IOException 
    ServerSocket server  = new ServerSocket(12345);
    ExecutorService pool = Executors.newFixedThreadPool(10);

    while (true) 
        Socket socket = server.accept();
        pool.execute(new MyRunnable(socket));
    

这是线程使用的Runnable 实现:

@Override
public void run() 
    try 
        SecureRandom random      = new SecureRandom();       // How to preallocate?
        BufferedInputStream bis  = new BufferedInputStream(mSocket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(mSocket.getOutputStream());
        TlsServerProtocol proto  = new TlsServerProtocol(bis, bos, random);
        MockPSKTlsServer server  = new MockPSKTlsServer();   // How to preallocate?
        proto.accept(server);
        Streams.pipeAll(proto.getInputStream(), System.out);
        proto.close();
     catch (IOException e) 
        System.err.print(e);
    

如何预分配Runnable使用的SecureRandomMockPSKTlsServer对象?

即如何在 main() 中创建 10 个这两个对象,然后在 run() 中重用它们?

【问题讨论】:

您使用哪个版本的 Java? 我在 Windows 和 Linux 上使用 Java SE 1.8 【参考方案1】:

在您的情况下,我将为每个类(SecureRandomMockPSKTlsServer)使用 ThreadLocal,为连接池的每个线程拥有一个专用的 SecureRandomMockPSKTlsServer 实例,并在以下情况下重用它们线程必须执行相同类型的任务,但输入不同 Socket,类似于:

private static final ThreadLocal<SecureRandom> random = ThreadLocal.withInitial(
    SecureRandom::new
);
private static final ThreadLocal<MockPSKTlsServer> server = ThreadLocal.withInitial(
    MockPSKTlsServer::new
);

...
public void run() 
    try (BufferedInputStream bis  = new BufferedInputStream(mSocket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(mSocket.getOutputStream());
        TlsServerProtocol proto  = new TlsServerProtocol(bis, bos, random.get())) 
        // Calling server.get() will get the instance dedicated to the current
        // thread, if no instance exists so far for this thread
        // new MockPSKTlsServer() will automatically be called then
        // affected to this thread for subsequent get's calls
        proto.accept(server.get());
        ...
     catch (IOException e) 
        System.err.print(e);
    

注意:使用try-with-resources 语句自动关闭您的输入/输出流。

【讨论】:

如果我在 MyRunnable.java 中创建 randomserver 变量 static 并拥有该类的 10 个实例 - 它们不会被所有 10 个实例使用?我很好奇你为什么在这里建议 static ?因为我需要为池中的每个线程预先分配 10 个 SecureRandomMockPSKTlsServer 实例... 不,它必须是 static 否则您的 ThreadLocal 将毫无用处。您应该阅读有关 ThreadLocal 的 javadoc,简而言之,ThreadLocal 用于将给定类的实例范围限定为 Thread,换句话说,线程 1 从 ThreadLocal 获得的内容会有所不同从线程 2 将得到什么。因此,如果您有 10 个线程在 ThreadLocal 上调用 get(),则 ThreadLocal 实际上将管理您的类的 10 个实例(每个线程一个)。【参考方案2】:

通常我会使用 ThreadLocal&lt;&gt; 并使用轻量级 Pool&lt;T&gt; 类来保存和提供实例。

我认为没有开箱即用的Pool&lt;T&gt;,但这只是简单的构造。

ThreadLocal&lt;&gt; 的唯一考虑是您必须确保正确释放回Pool&lt;&gt;。因此,如果您正在考虑将其传达给另一个线程,更好的方法可能是共享静态池,但可以使用锁定(如果您不太关心性能)或并发容器(如果您这样做的话)......

话虽如此:http://commons.apache.org/proper/commons-pool/

【讨论】:

其实我不和其他线程通信。我读取传入的数据,解密并关闭套接字。从长远来看,我想通过另一个套接字将解密的数据转发到嵌入式 Jetty(不能TLS PSK)。 IE。我正在尝试为 Jetty 编写线程池“反向代理”。 好吧,你不需要一个池,看起来你只需要一个实例 - 在这种情况下 - 忽略关于池的东西并将它直接粘贴在 ThreadLocal&lt;&gt; 中..跨度>

以上是关于如何为线程池服务器预分配对象?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 中如何处理并发请求(使用线程、线程池或子进程)

内存池进程池线程池

多线程池化分配器

详述Java线程池实现原理

详述Java线程池实现原理

线程池