外部共享资源(智能卡)的Java并发模式

Tob*_*bia 18 java concurrency multithreading smartcard threadpoolexecutor

我有一个Web服务器服务,客户端请求智能卡计算并获得结果.可用的智能卡号可以在服务器正常运行时间内减少或增加,例如我可以从阅读器中物理添加或删除智能卡(或许多其他事件......例如异常等).

在此输入图像描述

智能卡计算可能需要一段时间,因此如果存在对Web服务器的并发请求,我必须优化这些作业以使用所有可用的智能卡.

我想过使用智能卡线程池.至少对我而言,不寻常的是,池应该改变其大小,而不是取决于客户端请求,而只取决于智能卡的可用性.

在此输入图像描述

我研究过很多例子:

  • BlockingQueue:存储请求和停止线程等待某事做的好看.
  • FutureTask:我可以使用这个类让客户端等待它的答案,但是哪种类型的执行器应该完成任务?
  • ThreadPoolExecutor:看起来我需要什么,但有了这个我不能改变池大小,而且每个线程都应该链接到一个智能卡插槽.如果我可以更改池大小(在插入智能卡时添加线程并在移除智能卡时删除线程)以及我是否可以为每个线程分配特定智能卡,这可以是一种解决方案.

这是智能卡控件,每个智能卡有一个SmartcardWrapper,每个智能卡都有自己的插槽号.

public class SmartcardWrapper{

    private int slot;

    public SmartcardWrapper(int slot) {
        this.slot=slot;
    }   

    public byte[] compute(byte[] input) {
        byte[] out=new byte[];
        SmartcardApi.computerInput(slot,input,out); //Native method
        return out;
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用每个智能卡一个线程创建一个线程池:

private class SmartcardThread extends Thread{

    protected SmartcardWrapper sw;

    public SmartcardThread(SmartcardWrapper sw){
        this.sw=sw;
    }

    @Override
    public void run() {
        while(true){
            byte[] input=queue.take();
            byte output=sw.compute(input);
            // I have to return back the output to the client
        }           
    }
}
Run Code Online (Sandbox Code Playgroud)

每个人在同一输入队列中等待某事:

BlockingQueue<byte[]> queue=new BlockingQueue<byte[]>();
Run Code Online (Sandbox Code Playgroud)

但是如何将智能卡线程的输出返回给webserver-client呢?这让我觉得BlockingQueue不是我的解决方案.

如何解决这个问题?我应该遵循哪种并发模式?为每个智能卡分配一个线程是否正确或我是否可以简单地使用信号量?

Rav*_*abu 6

你的假设:

ThreadPoolExecutor:看起来我需要什么,但有了这个我不能改变池大小,而且每个线程都应该链接到一个智能卡插槽.

是不正确的.

You can set thread pool size dynamically.

看看下面的ThreadPoolExecutor API

public void setMaximumPoolSize(int maximumPoolSize)
Run Code Online (Sandbox Code Playgroud)

设置允许的最大线程数.这将覆盖构造函数中设置的任何值.如果新值小于当前值,则当下一个空闲时,多余的现有线程将被终止.

public void setCorePoolSize(int corePoolSize)
Run Code Online (Sandbox Code Playgroud)

设置核心线程数.这将覆盖构造函数中设置的任何值.如果新值小于当前值,则当下一个空闲时,多余的现有线程将被终止.如果需要更大,则新线程将启动以执行任何排队任务.

Core and maximum pool sizes:
Run Code Online (Sandbox Code Playgroud)

A ThreadPoolExecutor将根据corePoolSize和设置的边界自动调整池大小maximumPoolSize.

当在方法中提交新任务execute(java.lang.Runnable)corePoolSize运行的线程少于正在运行时,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求.

如果运行的线程数多于corePoolSize但少于maximumPoolSize线程,则仅当队列已满时才会创建新线程.

通过设置maximumPoolSize基本上无限制的值,例如Integer.MAX_VALUE,允许池容纳任意数量的并发任务.但我不建议拥有那么多线程.请谨慎设置此值.

最典型的情况是,核心和最大池大小仅在构造时设置,但也可以使用setCorePoolSize(int)和动态更改setMaximumPoolSize(int).

编辑:

为了更好地利用线程池,如果知道最大卡数为6,则可以使用

 ExecutorService executor = Executors.newFixedThreadPool(6);
Run Code Online (Sandbox Code Playgroud)

要么


Vla*_* G. 5

您是否考虑过使用Apache Commons Pool

您需要维护一个SmartcardWrapper对象池,其中每个SmartcardWrapper将代表一个物理SmartCard.无论何时需要进行新计算,都要从池中借用对象,进行计算并返回池中的对象,以便下一个线程可以重用它.

池本身是线程安全的,并在没有可用对象时阻塞.您需要做的就是实现一个api来向池中添加/删除SmartcardWrapper对象.


sha*_*zin 2

通过查看需求,最好的架构是将智能卡的计算与 Web 服务分离。

依赖 Web 服务等待处理器密集型任务将导致超时。

最好的解决方案是使用定期作业预计算智能卡,并将这些槽、计算对存储在 Redis 等缓存服务器中。

在此输入图像描述

智能卡同步器作业是一个单独的 J2SE 独立应用程序,它定期检查哪个智能卡可用且处于活动状态(无错误),并使用插槽和计算作为键/值对更新 Redis 缓存。如果存在不可用的智能卡,则会将其从缓存中删除。

Web 服务将仅检查 Redis 缓存中的特定槽键,如果找到值将返回该值,否则返回未找到该槽的值(不可用或错误)

该设计在智能卡端和客户端请求端都是可扩展的。