Grails / java单一资源池化线程队列

时间:2012-01-22 11:08:57

标签: java grails threadpool

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

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

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

有没有人知道如何解决这个问题?

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

    private static final Semaphore ticket = new Semaphore(1, true);
    ...
    public int ReadInt(int min, int max) throws QuantisException {
        while (true) {
            try {
                ticket.acquire();
                int data = QuantisReadScaledInt(deviceType.getType(), deviceNumber, min, max);
                ticket.release();
                return data;
            } catch (InterruptedException ex) {
            } catch (QuantisException ex) {
                ticket.release();
                throw ex;
            }
        }
    }

1 个答案:

答案 0 :(得分:1)

首先,“它会崩溃”对所发生的事情描述不佳。有异常吗?究竟发生了什么?

其次,您确定需要同步对API的访问。如果它提供了Java API,那么这个API很可能已经同步,并且不需要你的信号量。

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

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

第四:我不明白while(true)循环的要点。它循环的唯一时间是InterruptedException。并且InterruptedException正好用于表示线程应该停止执行ASAP。所以你的方法应该抛出这个异常,而不是吞下它。

最后,您应该学习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);
    }
}