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

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

enter image description here

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

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

enter image description here

我研究了很多例子:

  • BlockingQueue:存储请求并停止线程等待某些事情似乎很好。
  • FutureTask:我可以使用这个类让客户端等待它的答案,但是哪种类型的执行器应该执行该任务呢?
  • ThreadPoolExecutor:似乎是我需要的,但是有了这个,我无法更改池大小,而且每个线程都应该链接到一个智能卡插槽。如果我可以更改池大小(在插入智能卡时添加线程,在删除智能卡时删除线程),并且如果我可以为每个线程分配特定的智能卡,则可以解决此问题。

这是智能卡控件,每个智能卡都有一个智能卡包装器,每个智能卡都有自己的插槽号。

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;
    }
}

我尝试创建一个线程池,每个智能卡有一个线程:

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
        }           
    }
}

每个人在同一输入队列中等待某些内容:

BlockingQueue<byte[]> queue=new BlockingQueue<byte[]>();

但是如何将输出从智能卡线程返回到Web服务器客户端?这让我认为BlockningQueue不是我的解决方案。

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


答案 1

您的假设:

ThreadPoolExecutor:似乎是我需要的,但是有了这个,我无法更改池大小,而且每个线程都应该链接到一个智能卡插槽。

是不对的。

You can set thread pool size dynamically.

看看下面的 ThreadPoolExecutor API

public void setMaximumPoolSize(int maximumPoolSize)

设置允许的最大线程数。这将重写构造函数中设置的任何值。如果新值小于当前值,则多余的现有线程将在下次空闲时终止。

public void setCorePoolSize(int corePoolSize)

设置线程的核心数。这将重写构造函数中设置的任何值。如果新值小于当前值,则多余的现有线程将在下次空闲时终止。如果较大,则将启动新线程(如果需要)以执行任何排队的任务。

Core and maximum pool sizes:

A 将根据 和 设置的边界自动调整池大小。ThreadPoolExecutorcorePoolSizemaximumPoolSize

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

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

通过设置为实质上无界的值(如 ),允许池容纳任意数量的并发任务。但我不建议拥有那么多线程。请谨慎设置此值。maximumPoolSizeInteger.MAX_VALUE

大多数情况下,核心池和最大池大小仅在构造时设置,但它们也可以使用 ) 和 动态更改。setCorePoolSize(intsetMaximumPoolSize(int)

编辑:

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

 ExecutorService executor = Executors.newFixedThreadPool(6);


答案 2

你有没有考虑过使用Apache Commons Pool

您需要维护一个智能卡包装器对象池,其中每个智能卡包装程序将表示一个物理智能卡。每当需要进行新的计算时,您都可以从池中借用对象,进行计算并在池中返回对象,以便下一个线程可以重用它。

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


推荐