【问题标题】:Grails/java single resource pooling thread queueGrails/java 单资源池线程队列
【发布时间】:2012-01-22 11:08:57
【问题描述】:

我正在尝试设置一个从设备Quantis - Quantum Random Number Generator 提供真实随机数的服务器。我使用 Grails 框架构建了 Web 应用程序,在 Tomcat 服务器上运行 Ubuntu

由于只有 1 个设备,我必须安排访问它的线程,对吗?所以我在调用这个设备的函数(ReadInt, ReadDouble, ReadFloat)上设置了信号量(有 1 个资源)。包含这些函数的对象称为Quantis,存储在 Grails 应用程序的 Java 源包中,它被实现为单例;然后控制器将调用该对象及其函数的实例。然后这些函数中的每一个都将调用系统上的 Quantis 库以从设备读取流

信号量似乎工作正常。但是,如果我非常快地刷新页面(检索随机数流)(比如 +/- 10 次),它就会崩溃。我已经“盲目地”尝试了许多来自互联网的方法,包括 grails executors,但似乎没有任何效果(但是我可能没有正确实现它们,真的)。

有人知道我该如何解决这个问题吗?

这是我的其中一个函数的代码:(它们看起来都差不多,但在检索数据时调用不同的系统库函数)

私有静态最终信号量票=新信号量(1,真); ... 公共 int ReadInt(int min, int max) 抛出 QuantisException { 而(真){ 尝试 { 票证.acquire(); int data = QuantisReadScaledInt(deviceType.getType(), deviceNumber, min, max); 票。释放(); 返回数据; } 捕捉(InterruptedException ex){ } 捕捉(QuantisException ex){ 票。释放(); 扔前; } } }

【问题讨论】:

    标签: java grails threadpool


    【解决方案1】:

    首先,“它会崩溃”对所发生的事情的描述很糟糕。有什么例外吗?究竟发生了什么?

    第二,你确定要同步访问API吗?如果它提供了 Java API,则很有可能该 API 已经同步,并且不需要您的信号量。

    第三,如果你获得了一个信号量,你应该在 finally 块中释放它。这保证它被释放,无论 try 块内发生什么:

    ticket.acquire();
    try {
        ...
    }
    catch (...) 
    finally {
        ticket.release();
    }
    

    第四:我不明白while(true) 循环的意义。它循环的唯一时间是出现 InterruptedException 时。并且 InterruptedException 精确地用于向线程发出它应该尽快停止执行的信号。所以你的方法应该抛出这个异常而不是吞掉它。

    最后,您应该学习 Java 命名约定并遵守它们。方法以小写字母开头。

    如果您确实需要同步访问,那么我将如何重写该方法:

    public int readInt(int min, int max) throws QuantisException, InterruptedException {
        ticket.acquire();
        try {
            return quantisReadScaledInt(deviceType.getType(), deviceNumber, min, max);
        }
        finally {
            ticket.release();
        }
    }
    

    如果要确保只有一个线程可以访问本机库函数,请使用这样的类:

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    import java.util.concurrent.TimeUnit;
    
    public class SingleThreadAccess {
        public static final SingleThreadAccess INSTANCE = new SingleThreadAccess();
    
        private ExecutorService executor;
    
        // to be called by ServletContextListener.contextInitialized()
        public void init() {
            executor = Executors.newSingleThreadExecutor();
        }
    
        // to be called by ServletContextListener.contextDestroyed()
        public void shutdown() {
            executor.shutdown();
            try {
                executor.awaitTermination(2L,TimeUnit.SECONDS);
            }
            catch (InterruptedException e) {
            }
            executor.shutdownNow();
        }
    
        public int readInt(int min, int max) throws QuantisException, InterruptedException {
            Callable<Integer> task = new Callable<Integer>() {
                @Override
                public Integer call() throws QuantisException {
                    return quantisReadScaledInt(deviceType.getType(), deviceNumber, min, max);
                }
            };
            Future<Integer> future = executor.submit(task);
            try {
                future.get();
            }
            catch (ExecutionException e) {
                unwrap(e);
            }
        }
    
        private void unwrap(ExecutionException e) throws QuantisException {
            Throwable t = e.getCause();
            if (t instanceof QuantisException) {
                throw (QuantisException) t;
            }
            throw new RuntimeException(e);
        }
    }
    

    【讨论】:

    • 您好 Nizet,非常感谢您的意见。说crash,我的意思是当设备有多个连接请求时,tomcat服务器会crash,日志输出:
       libusb:warning [libusb_open] internal signaling write failed # # A fatal error has been detected by Java 运行时环境: # # SIGSEGV (0xb) at pc=0x00007f095e06e288, pid=9879, tid=139678215476992 # ... 
      但是当我实现信号量时,它实际上工作正常,直到我们使用多个连接发送测试它(刷新)请求真的非常快。谢谢,我会试试你刚才说的..
    • 哦,差点忘了:不,该 api 不是 java api。它是一个带有 java 包装器的 C 库。从我所看到的,我不认为它是同步的。此外,在我完全实现信号量之前,如果我真的快速刷新请求仅 3 次,它会立即崩溃。由于没有例外,并且错误消息说(在最后)错误来自 JVM 外部,我很确定它来自 C 库,因为多个正在请求设备,而它被锁定当前线程正在使用它。
    • 我不是本地代码方面的专家,但可能是 C 库,即使只有一个线程一次调用它,也不能被多个线程使用。在这种情况下,您应该专门使用一个线程来使用 API。如果是这种情况(但应该记录在案!),而您不知道该怎么做,请再问一个问题,我很乐意回答。
    • 天哪,非常感谢尼采特。是的,我不知道怎么做你刚才说的,这就是我想要实现的,我只是不知道我现在做的是否正确。我是吗?我确定 C 库中的线程没有同步,因为它只是简单地调用设备,并得到它的返回。另一件事是,tomcat 是否有可能创建超过 1 个 servlet,而线程同步只发生在 1 个特定的 servlet 内?因为我似乎无法理解为什么上面的代码(你给出的)在我快速刷新时仍然崩溃。
    • 如果 tomcat servlet 确实有这样的行为,您知道如何将其限制为仅创建一个 servlet,然后将请求排队以使用该 servlet 吗?如果没有,我们可以按照你刚才的建议去做吗?非常感谢,尼采特
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 2013-08-01
    • 1970-01-01
    • 2012-04-11
    • 2016-11-24
    • 2017-06-15
    相关资源
    最近更新 更多