Static method to be accessed by multiple threads, Java
I am using a third party library to do hand evaluation of 7 card poker hands. The method evaluate
in this library is declared as public static and I believe it alters some global static arrays within the class. The problem I have is that as I am doing an enumeration algorithm of about 10m enumerations I want to parallelize it, therefore I created Fu开发者_StackOverflowtureTasks which each which evaluate a fraction of the 10m evaluations. The error I am getting is:
java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: -2147483648
at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
Which from what I gather from Google searches is caused when attempting to retrieve the result of a task that aborted by throwing an exception.
Is there some way to make this static method thread safe, like each thread making a its own copy of the global static arrays that are being edited?
Thanks
You could load the library in a separate ClassLoader
for each thread, to ensure that each class has its own set of classes and therefore its own set of static variables.
If you do so, you must be careful to ensure that the parent class loader of those class loaders hasn't got access to the library, however.
If you could modify the code you could make the static variables thread local, but it doesn't sound like you can modify this part of the code. More on thread local storage (wikipedia)
Create a singleton instance wrapper for the library with a synchronized method decoration on the evaluate method
public final class SynchronizedHandEvaluator {
private static final SynchronizedHandEvaluator INSTANCE = new SynchronizedHandEvaluator();
public static final getInstance() {
return INSTANCE;
}
private SynchronizedHandEvaluator() { }
public synchronized int evaluate(Card[] hand) {
return ExternalLibrary.evaluate(hand);
}
}
// then just use the wrapper as you would expect
int result = SynchronizedHandEvaluator.getInstance().evaluate(hand);
Are you operating on the arrays safely? Can you explain the structure of you code better? How are you dividing up the work and why does the algorithm need to modify a global array?
Regardless, maybe using ForkJoin or a CyclicBarrier/Phaser to divide up the work would make more sense?
If you have a method being accessed by multiple threads with shared state (global static arrays), you need to make sure that they are accessed in a thread-safe manner.
This means that you need to synchronize/lock both when you read from these arrays and also when they are written to.
It sounds, just from your stacktrace, that you are not.
If possible, I would suggest minimizing or eliminating the amount of state shared between threads.
You can't make it thread-safe, but you could synchronize access to the library:
static Object globalLock = new Object();
synchronize (globalLock) {
evaluate();
}
That means, of course, that only one thread can execute this method at a time. So if your program spends a lot of time in that method, it won't help you.
Alternatively, you could make the instances run in separate processes. But establishing communication between the processes (via RMI or similar) is quite painful.
The only other option is to rewrite it, if you access to the source code. You'd have to move all static fields into a single context class, and then make sure that all code that used to access the static fields has a reference to an instance of the new context class.
The already mentioned ThreadLocal mechanism can help you with this, because it is a special kind of reference that looks different to every thread. This may save you from modifying method signatures to pass an instance to the context object. IMHO it's probably cleander to just pass the context into the methods. With the refactoring functions of modern IDEs it's not that difficult to do.
Use ConcurrentLinkedQueue
or Collections#synchronizedList()
instead of array. Its access is internally synchronized.
With millions of hands to evaluate, it's never too soon to look at Java Distributed Computing.
While you consider this other good suggestion, you could wrap the evaluator in a simple, single-threaded server and fire up as many JVMs as resources allow.
Finally, consider writing your own evaluator; you can test yours against the existing one, and yours may be better!
精彩评论