semantic of local final variable in the Java Memory Model? - java

The following code sometimes prints "valueWrapper.isZero()" on my Windows-PC and a Mac,
both running their JVM in server mode.
Ok this happens because the value field isn't final in the ValueWrapper class,
so its possible that some thread sees the stale value 0.
public class ConcurrencyApp {
private final Random rand = new Random(System.currentTimeMillis());
private ValueWrapper valueWrapper;
private static class ValueWrapper {
private int value;
public ValueWrapper(int value) {
this.value = value;
}
public boolean isZero() {
return value == 0;
}
}
private void go() {
while (true) {
valueWrapper = new ValueWrapper(randomInt(10, 1024));
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
if (valueWrapper.isZero()) {
System.out.println("valueWrapper.isZero()");
}
}
});
thread.start();
}
}
private int randomInt(int min, int max) {
int randomNum = rand.nextInt((max - min) + 1) + min;
return randomNum;
}
public static void printVMInfos() {
String vmName = System.getProperty("java.vm.name");
System.out.println("vm name: " + vmName);
int cores = Runtime.getRuntime().availableProcessors();
System.out.println("available cores: " + cores);
}
public static void main(String[] args) {
ConcurrencyApp app = new ConcurrencyApp();
printVMInfos();
app.go();
}
}
But what about the following modification, here i used a local final variable:
private void go() {
while (true) {
final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024));
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
if (valueWrapper.isZero()) {
System.out.println("valueWrapper.isZero()");
}
}
});
thread.start();
}
}
It looks like that now no thread sees a stale value of 0.
But is this guaranteed by the JMM?
A brief look in the spec doesn't convinced me.

It looks like that now no thread sees a stale value of 0. But is this guaranteed by the JMM? A brief look in the spec doesn't convinced me.
It is guaranteed but not because of the final. There is a happens-before guarantee when you fork a thread. Any memory operations done in the forking thread before you start a new thread are guaranteed to be seen by the new thread as fully constructed and published. To quote from JLS 17.4.4 - Synchronization Order:
An action that starts a thread synchronizes-with the first action in the thread it starts.
This is different from a final field when we are talking about object construction and publishing. If a field is final then it is guaranteed to be properly initialized when the constructor finishes and the object is published to multiple threads. In your case, the final is necessary because of the anonymous class. If you weren't using an anonymous class and then you could remove the final on your ValueWrapper, your object would still be guaranteed to be fully constructed because of the above.
FYI, see here for final field info: Java concurrency: is final field (initialized in constructor) thread-safe?

I am addressing one point Gray didn't, but I would accept his, as his answer is spot on
The following code sometimes prints "valueWrapper.isZero()" on my Windows-PC and a Mac, both running their JVM in server mode.... It looks like that now no thread sees a stale value of 0. But is this
guaranteed by the JMM? A brief look in the spec doesn't convinced me.
The reason you are seeing valueWrapper.isZero() returning true sometimes because valueWrapper is changing after the start is invoked and before run gets to the boolean test. If you only have one instance created it will always not be zero as Gray mentioned.
The reason final ValueWrapper valueWrapper = new ValueWrapper(randomInt(10, 1024)); works all the time is because the field is thread (and method) local and the semantics for a local object and anonymous inner classes is to copy the original reference into the class instance.

Related

Will JVM update value of field for all threads with usage of Happens-Before, without assignment value to field directly?

I already know that if I write to non volatile field from another thread, he could probably cache it so all other threads won't see actual value. But if I would invoke for example start() on thread object AFTER assignment value to filed, JVM will update this value for all other threads. But will JVM update value of this field if I will do same action but will assign value to field not directly, like this: object.field = 100 but with invoking method object.setFiled(100).
public class Main {
int value;
public static void main(String[] args) {
Main main = new Main();
main.value = 100;
new Thread(() -> System.out.println(main.getValue())).start();
In this case value of field will be same for all other threads for sure
}
public int getValue() {
return value;
}
}
public class Main {
private int value;
public static void main(String[] args) {
Main main = new Main();
main.setValue(100);
new Thread(() -> System.out.println(main.getValue())).start();
But will be result same in this case?
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
The mechanics of how something gets updated in a thread, whether it be direct field access or through setters doesn't matter in this case. JLS 17.4.5 states that "A call to start() on a thread happens-before any actions in the started thread." And within a single thread "If x and y are actions of the same thread and x comes before y in program order, then [x happens before y]". In this case a method setter or field assignment that would make a value visible to the original thread also makes that value visible to the newly started thread. Roughly, anything that was visible to the thread calling start() at the time start() was called will be similarly visible to the new thread as long as the object being viewed isn't mutated and the attempts to view state don't mutate things (eg. LinkedHashMap#get with accessOrder=true).

Safe publication of a ConcurrentHashMap into a class field

I am trying to write a test that demonstrates that assigning a new reference to a class' field in a multi-threading environment is not thread-safe and more specifically has visibility problems if that field is not declared as volatile or AtomicReference.
The scenario I use is a PropertiesLoader class (shown below), which is supposed to load a set of properties (currently only one property is used) stored in a Map<String, String> and also tries to support reloading. So there are many threads reading a property and at some point in time another thread is reloading a new value that needs to be visible to the reading threads.
The test is intended to work as following:
it invokes the reader threads which are spin-waiting until they "see"
the property value change
at some point the writer thread creates a new map with a new value for the property and assigns that map to the field in question (PropertyLoader.propertiesMap)
if all reader threads see the new value the test is completed otherwise it hangs forever.
Now I know that strictly speaking, there is no test that can prove the thread-safeness of some code (or the lack of it) but in this case I feel like it should be relatively easy to demonstrate the problem at least empirically.
I have tried using a HashMap implementation to store the properties and in this case the test hangs as expected even if I use only one reading thread.
If however, a ConcurrentHashMap implementation is used, the test never hangs no matter how many reading threads are being used (I have also tried waiting randomly in the reader threads with no success).
As far as my understanding goes, the fact that ConcurrentHashMap is thread-safe should not affect the visibility of the field where it is assigned to. So volatile/AtomicReference is still required for that field. However the above test seems to contradicts this since it behaves as if the map is always safely published without the need of additional synchronization.
Is my understanding wrong? Perhaps ConcurrentHashMap makes some additional synchronization promises that I am not aware of?
Any help would be highly appreciated.
P.S. The code below should be executable as is as a Junit test. I have run it in a machine with AMD Ryzen 5, Windows 10, JDK 1.8.0_201 and in a second machine i7 Intel, Fedora 30, JDK 1.8.xx (not remember the exact version of JDK) with the same results.
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
public class PropertiesLoaderTest {
private static final String NEW_VALUE = "newValue";
private static final String OLD_VALUE = "oldValue";
private static final String PROPERTY = "property";
/**
* Controls if the reference we are testing for visibility issues ({#link PropertiesLoader#propertyMap} will
* be assigned a HashMap or ConcurrentHashMap implementation during {#link PropertiesLoader#load(boolean)}
*/
private static boolean USE_SIMPLE_MAP = false;
#Test
public void testReload() throws Exception {
PropertiesLoader loader = new PropertiesLoader();
Random random = new Random();
int readerThreads = 5;
int totalThreads = readerThreads + 1;
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch finishLatch = new CountDownLatch(totalThreads);
// start reader threads that read the property trying to see the new property value
for (int i = 0; i < readerThreads; i++) {
startThread("reader-thread-" + i, startLatch, finishLatch, () -> {
while (true) {
String value = loader.getProperty(PROPERTY);
if (NEW_VALUE.equals(value)) {
log("Saw new value: " + value + " for property: " + PROPERTY);
break;
}
}
});
}
// start writer thread (i.e. the thread that reloads the properties)
startThread("writer-thread", startLatch, finishLatch, () -> {
Thread.sleep(random.nextInt(500));
log("starting reload...");
loader.reloadProperties();
log("finished reload...");
});
log("Firing " + readerThreads + " threads and 1 writer thread...");
startLatch.countDown();
log("Waiting for all threads to finish...");
finishLatch.await();
log("All threads finished. Test successful");
}
static class PropertiesLoader {
// The reference in question: this is assigned in the constructor and again when calling reloadProperties()
// It is not volatile nor AtomicReference so there are visibility concerns
Map<String, String> propertyMap;
PropertiesLoader() {
this.propertyMap = load(false);
}
public void reloadProperties() {
this.propertyMap = load(true);
}
public String getProperty(String propertyName) {
return propertyMap.get(propertyName);
}
private static Map<String, String> load(boolean isReload) {
// using a simple HashMap always hang the test as expected: the new reference cannot be
// seen by the reader thread
// using a ConcurrentHashMap always allow the test to finish no matter how many reader
// threads are used
Map<String, String> newMap = USE_SIMPLE_MAP ? new HashMap<>() : new ConcurrentHashMap<>();
newMap.put(PROPERTY, isReload ? NEW_VALUE : OLD_VALUE);
return newMap;
}
}
static void log(String msg) {
//System.out.println(Thread.currentThread().getName() + " - " + msg);
}
static void startThread(String name, CountDownLatch start, CountDownLatch finish, ThreadTask task) {
Thread t = new Thread(new ThreadTaskRunner(name, start, finish, task));
t.start();
}
#FunctionalInterface
interface ThreadTask {
void execute() throws Exception;
}
static class ThreadTaskRunner implements Runnable {
final CountDownLatch start;
final CountDownLatch finish;
final ThreadTask task;
final String name;
protected ThreadTaskRunner(String name, CountDownLatch start, CountDownLatch finish, ThreadTask task) {
this.start = start;
this.finish = finish;
this.task = task;
this.name = name;
}
#Override
public void run() {
try {
Thread.currentThread().setName(name);
start.await();
log("thread started");
task.execute();
log("thread finished successfully");
} catch (Exception e) {
log("Error: " + e.getMessage());
}
finish.countDown();
}
}
}
It's a bit worse than you might think but there is also a saving grace.
The bit worse part: constructors are not synchronized. In this case that means that the PropertiesLoader.propertyMap which is created in the constructor is not guaranteed to be visible to the other threads (reader or writer). Your saving grace here is the CountDownLatches you use (these establish a happen-before relation) as well as the Thread.start (which also establish a happen-before relation) . Also, in practice "constructors are not synchronized" is rarely a problem and difficult to reproduce (see also test-code below). For more information on the matter, please read this question. Conclusion is that the PropertiesLoader.propertyMap must either be volatile / AtomicReference or final (final could be used in combination with the ConcurrentHashMap).
The reason you cannot reproduce the synchronization issue with a ConcurrentHashMap is the same reason it is difficult to reproduce the "constructors are not synchronized" problem: a ConcurrentHashMap uses synchronization internally (see this answer) which triggers a memory flush that not only makes the new values in the map visible to other threads, but also the new PropertiesLoader.propertyMap value.
Note that a volatile PropertiesLoader.propertyMap will guarantee (and not just make it likely) that new values are visible to other threads (ConcurrentHashMap is not required, see also this answer). I usually set these kind of maps to a read-only map (with the help of Collections.unmodifiableMap()) to broadcast to other programmers that this is not an ordinary map that can be updated or changed at will.
Below some more test-code which tries to eliminate as much synchronization as possible. The end-result for the test is exactly the same but it also shows the side-effect of having a volatile boolean in a loop and that the non-null assignment of propertyMap somehow is always seen by other threads.
package so;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class MapVisibility {
static int readerThreadsAmount = 2;
public static void main(String[] args) {
ExecutorService executors = Executors.newFixedThreadPool(readerThreadsAmount);
try {
new MapVisibility().run(executors);
} catch (Exception e) {
e.printStackTrace();
} finally {
executors.shutdownNow(); // Does not work on FAIL, manually kill reader-task from task-manager.
}
}
//final boolean useConcurrentMap = false;
// When ConcurrentHashMap is used, test is always a success.
final boolean useConcurrentMap = true;
final boolean useStopBoolean = false;
// When volatile stop boolean is used, test is always a success.
//final boolean useStopBoolean = true;
//final boolean writeToConsole = false;
// Writing to System.out is synchronized, this can make a test succeed that would otherwise fail.
final boolean writeToConsole = true;
Map<String, String> propertyMap;
// When the map is volatile, test is always a success.
//volatile Map<String, String> propertyMap;
final String oldValue = "oldValue";
final String newValue = "newValue";
final String key = "key";
volatile boolean stop;
void run(ExecutorService executors) throws Exception {
IntStream.range(0, readerThreadsAmount).forEach(i -> {
executors.execute(new MapReader());
});
sleep(500); // give readers a chance to start
setMap(oldValue);
sleep(100); // give readers a chance to read map
setMap(newValue);
sleep(100); // give readers a chance to read new value in new map
executors.shutdown();
if (!executors.awaitTermination(100L, TimeUnit.MILLISECONDS)) {
System.out.println("FAIL");
stop = true;
} else {
System.out.println("Success");
}
}
void setMap(String value) {
Map<String, String> newMap = (useConcurrentMap ? new ConcurrentHashMap<>() : new HashMap<>());
newMap.put(key, value);
propertyMap = newMap;
}
class MapReader implements Runnable {
#Override
public void run() {
print("Reader started.");
final long startTime = System.currentTimeMillis();
while (propertyMap == null) {
// In worse case, this loop should never exit but it always does.
// No idea why.
sleep(1);
}
print((System.currentTimeMillis() - startTime) + " Reader got map.");
if (useStopBoolean) {
while (!stop) {
if (newValue.equals(propertyMap.get(key))) {
break;
}
}
} else {
while (true) {
if (newValue.equals(propertyMap.get(key))) {
break;
}
}
}
print((System.currentTimeMillis() - startTime) + " Reader got new value.");
}
}
void print(String msg) {
if (writeToConsole) {
System.out.println(msg);
}
}
void sleep(int timeout) {
// instead of using Thread.sleep, do some busy-work instead.
final long startTime = System.currentTimeMillis();
Random r = new Random();
#SuppressWarnings("unused")
long loopCount = 0;
while (System.currentTimeMillis() - startTime < timeout) {
for (int i = 0; i < 100_000; i++) {
double d = r.nextDouble();
double v = r.nextDouble();
#SuppressWarnings("unused")
double dummy = d / v;
}
loopCount++;
}
//print("Loops: " + loopCount);
}
}

Simulating Field-visibility problem in Java

I was going through one of the tutorials on memory model of Java and came across this concept of field visibility which happens in multi-threaded programming. I tried to simulate the same using the below code, however , I see in each thread, the latest value is being reflected (in ReaderThread).
The below is the complete program.
Edit
After some suggestion to use while(somevariable), I incorporated, but still getting the same behaviour. I removed sysout on reading the x
FieldVisibility.java
package com.example.threads.fieldvisibility;
public class FieldVisibility {
private int x;
private boolean condition;
public FieldVisibility() {
condition = true;
}
public void reader() {
System.out.println("x in reader() is " + x);
}
public void writer() {
x++;
}
public boolean getCondition() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
ReaderThread.java
package com.example.threads.fieldvisibility;
public class ReaderThread extends Thread {
private FieldVisibility fv;
public ReaderThread(FieldVisibility fv) {
this.fv = fv;
}
#Override
public void run() {
while (fv.getCondition()) {
System.out.println("It mean condition is true, which was set initially");
}
for (;;) {
}
}
}
WriterThread.java
package com.example.threads.fieldvisibility;
public class WriterThread extends Thread {
private FieldVisibility fv;
public WriterThread(FieldVisibility fv) {
this.fv = fv;
}
#Override
public void run() {
fv.setCondition(false);
for (;;) {
fv.writer();
}
}
}
MainApp.java
package com.example.threads.fieldvisibility.main;
import com.example.threads.fieldvisibility.FieldVisibility;
import com.example.threads.fieldvisibility.ReaderThread;
import com.example.threads.fieldvisibility.WriterThread;
public class MainApp {
public static void main(String[] args) throws InterruptedException {
FieldVisibility fv = new FieldVisibility();
ReaderThread rt = new ReaderThread(fv);
WriterThread wt = new WriterThread(fv);
wt.start();
rt.start();
Thread.sleep(999999999L);
}
}
Edit
I added a new variable condition in FieldVisibility, whose default values is true. Next, I set its value to false in WriterThread, however, the same value (false) is still propagated to ReaderThread, so I still am not able to simulate it.
Original
I expected that at some time ReaderThread won't be able to "see" the latest value of variable x, but I saw every time I run it, it gave same results. I even run in debug mode, suspended ReaderThread while running WriterThread continuously. But that also didn't prevent ReaderThread to have latest values. I expected that I need to declare variable x as volatile in order for ReaderThread to read latest values of x.
How can I simulate the field visibility concept, or what changes I need to do for this?
Your example doesn't work because System.out.println() uses a shared resource (System.out), so it will synchronize with other uses of the same resource.
Therefore you will never* see a result where one thread uses the old value of the other. (*in theory it is possible for the reader to read x between x++ and the corresponding System.out.println()
Here is an example where a old value is used:
public class ThreadVisibility implements Runnable {
private boolean stop = false;
#Override
public void run() {
while (!stop);
}
public static void main(String[] args) throws InterruptedException {
ThreadVisibility test = new ThreadVisibility();
Thread t = new Thread(test);
t.setDaemon(true);
System.out.println("Starting Thread");
t.start();
Thread.sleep(1000);
System.out.println("Stopping Thread");
test.stop = true;
t.join(1000);
System.out.println("Thread State: " + t.getState());
}
}
If you run this code, it will display that the thread is still running at the end. Without the t.setDaemon(true), the VM would wait for the Thread to finish, which would never happen.
If you comment out the Thread.sleep, then the new Thread may terminate (at least it did in my tests), but it is not guaranteed to.
The right fix for this problem is to declare stop volatile.
Or add a memory barrier.

need a simple example for the synchronization

public class Test implements Runnable{
private String name;
public Test(String name){
this.name = name;
}
public void run() {
blah(name);
}
public synchronized void blah(String obj) {
System.out.println("Here: "+obj);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Test x = new Test("X");
Test y = new Test("Y");
Thread tx = new Thread(x);
Thread ty = new Thread(y);
tx.start();
ty.start();
}
This example should help me to understand synchronization, but I don't. This is because if I remove the word synchronize, it printed the same output (random)
Synchronization is irrelevant here because your two threads are each synchronizing on their own Runnable. There is no shared lock, and no shared data.
If you pass the same Runnable instance into each Thread then they will share the same lock. If your Runnable does something in a thread-unsafe way (like using ++ to increment a shared variable (an instance variable of the Runnable), or adding the entry to a shared ArrayList) then you can create a situation where removing synchronization can make the code break (with the understanding that breakage may not happen reliably, that's what makes multithreaded programming fun).
Making toy examples like this is not a good preparation for real-life multithreading. Threads shouldn't be in the business of implementing locking, they should be accessing data objects that enforce their own invariants.
Your example is technically correct, but there is no timing dependent conflict in your synchronized block. As such, there is no chance that you will see different output, regardless of the ordering of the calls.
In addition, you create two resources, and there is no cross-thread communication between the two resources, so effectively you've tested two synchronized blocks once each.
You need an example that can break when not synchronized.
Here is an example that can break
public class Counter {
int count;
public Counter() {
count = 0;
}
public int getCount() {
return count;
}
public /* need synchronized here */ void update(int value) {
int buffer = 0;
buffer = buffer + count;
buffer = buffer + value;
count = buffer;
}
}
public class UpdateCounter extends Thread {
public UpdateCounter(Counter counter, int amount) {
this.counter = counter;
this.name = name;
}
public void run() {
System.out.printf("Adding %d to count\n", amount);
counter.update(amount);
System.out.printf("Count is %d\n", counter.getCount());
}
}
public static void main(String[] args) {
Counter counter = new Counter();
UpdateCounter x = new UpdateCounter(counter, 30);
UpdateCounter y = new UpdateCounter(counter, 100);
x.start();
y.start();
}
With an example like this, one would eventually see a series of lines that indicated some value was being added to the counter, but the counter would update by the wrong value.
This is because one thread will eventually get paused with a buffer holding the "next" value, and the other thread will race across the same block of code, storing its "next" value into count. Then the paused thread will un-pause, and store its "next" value effectively removing the amount added by the thread that raced ahead of it.
By adding the synchronized keyword, only one thread is allowed entry into the update block, and the race condition I described above cannot occur.
Note that this is an example that can fail with bad synchronization, and not a good way to implement a counter.

Java Concurrent Collection Search

I've been programming in Java for sometime but new to concurrent programming, so bear with me!
I'm trying to develop a class that holds a group of Collection classes [eg ArrayLists] and then to find a specified value it traverses all collections at the same time, stopping all threads if it finds the given value.
I've pasted my code below and was wondering if anyone knows how within contains_multiple_collections() I catch if one of the threads returned Futures has returned true?
Thanks
Graham
public class CollectionGroup<V> extends ContainerGroup
{
//...
public boolean contains(V value)
{
boolean containsValue = false;
if (mCollections.size() == 1)
{
containsValue = mCollections.get(0).contains(value);
}
else
{
containsValue = contains_multiple_collections(value);
}
return containsValue;
}
private boolean contains_multiple_collections(V value)
{
// thread pool
int numberProcessors = mCollections.size();
ExecutorService es = Executors.newFixedThreadPool(numberProcessors);
for (int i=0; i<numberProcessors; i++)
{
AbstractCollection<V> collection = mCollections.get(i);
MyCallable callable = new MyCallable(collection,value);
Future<Boolean> future = es.submit(callable);
//...
}
return true;
}
private class MyCallable implements Callable<Boolean>
{
protected AbstractCollection<V> mCollection;
protected V mValue;
public MyCallable(AbstractCollection<V> collection, V value)
{
mCollection = collection;
mValue = value;
}
#Override
public Boolean call() throws Exception
{
boolean ok = mCollection.contains(mValue);
return ok;
}
} // class MyCallable
} // class CollectionGroup
contains won't stop just because you might have found the value in another thread. The only way to do this is to loop yourself.
For a CPU intensive process, the optimal number of threads is likely to be the number of cores you have. Creating too many threads adds overhead which slows down your solution.
You should also remember to shutdown() the ExecutorService when you are finished with it.
If you want to speed up the search, I would maintain a Set of all values this is likely to be 10-100x faster than using multiple threads.
You could use an ExecutorCompletionService. Just keep take()ing (take() blocks until a completed Future is present) until you get a result that is true and shutdownNow() the underlying ExecturService once you've found something. This won't immediately stop all threads once a value is found though.
The issue is that your contains_multiple_collections method does not wait for the search to complete. You have two options I can think of. The first would involve some asynchronous callback implementation where the contains method does not block and perhaps takes a callback/listener object as an argument. The second is to make the contains method block until an outcome has been determined. I've outlined a sample implementation for latter approach below, it's not tested so be careful...
/*
* contains_multiple_collections now blocks until the desired
* value is located or all searches have completed unsuccessfully...
*/
private boolean contains_multiple_collections(V value) {
// thread pool
int numberProcessors = mCollections.size();
ExecutorService es = Executors.newFixedThreadPool(numberProcessors);
Object lock = new Object();
AtomicBoolean outcome = new AtomicBoolean(false);
AtomicInteger remainingSearches = new AtomicInteger(mCollections.size());
for (int i = 0; i < numberProcessors; i++) {
AbstractCollection<V> collection = mCollections.get(i);
es.submit(new MyRunnable(collection, value, lock, outcome, remainingSearches));
}
/* Wait for searches to run. This thread will be notified when all searches
* complete without successfully locating the value or as soon as the
* desired value is found.
*/
synchronized (lock) {
while (!outcome.get() && remainingSearches.get() > 0) {
try {
lock.wait();
} catch (InterruptedException ex) {
// do something sensible.
}
}
es.shutdownNow();
}
return outcome.get();
}
private class MyRunnable implements Runnable {
final AbstractCollection<V> mCollection;
final V mValue;
final Object lock;
final AtomicBoolean outcome;
final AtomicInteger remainingSearches;
public MyRunnable(AbstractCollection<V> mCollection, V mValue,
Object lock, AtomicBoolean outcome, AtomicInteger remainingSearches) {
this.mCollection = mCollection;
this.mValue = mValue;
this.lock = lock;
this.outcome = outcome;
this.remainingSearches = remainingSearches;
}
public void run() {
boolean ok = mCollection.contains(mValue);
if (ok || remainingSearches.decrementAndGet() == 0) {
synchronized (lock) {
if (ok) {
outcome.set(true);
}
lock.notify();
}
}
}
}
You could repeatedly loop through all the futures and poll them with get, using zero or almost zero timeout, but probably a better idea is to provide a callback to all your MyCallables, which should then call it when a match is found. The callback should then cancel all other tasks, maybe by shutting down the ExecutorService.
I suggest using a static AtomicBoolean which is set when a match is found. Each thread could then check the value before proceeding.

Categories