This question is NOT about how to use a ThreadLocal. My question is
about the side effect of the ForkJoinPool continuation of ForkJoinTask.compute() which breaks the ThreadLocal contract.
In a ForkJoinTask.compute(), I pull an arbitrary static ThreadLocal.
The value is some arbitrary stateful object but not stateful beyond the end of the compute() call. In other words, I prepare the threadlocal object/state, use it, then dispose.
In principle you would put that state in the ForkJoinTasK, but just assume this thread local value is in a 3rd party lib I cannot change. Hence the static threadlocal, as it is a resource that all tasks instances will share.
I anticipated, tested and proved that simple ThreadLocal gets initialized only once, of course. This means that due to thread continuation beneath the ForkJoinTask.join() call, my compute() method can get called again before it even exited. This exposes the state of the object being used on the previous compute call, many stackframes higher.
How do you solve that undesirable exposure issue?
The only way I currently see is to ensure new threads for every compute() call, but that defeats the F/J pool continuation and could dangerously explode the thread count.
Isn't there something to do in the JRE core to backup TL that changed since the first ForkJoinTask and revert the entire threadlocal map as if every task.compute is the first to run on the thread?
Thanks.
package jdk8tests;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.atomic.AtomicInteger;
public class TestForkJoin3 {
static AtomicInteger nextId = new AtomicInteger();
static long T0 = System.currentTimeMillis();
static int NTHREADS = 5;
static final ThreadLocal<StringBuilder> myTL = ThreadLocal.withInitial( () -> new StringBuilder());
static void log(Object msg) {
System.out.format("%09.3f %-10s %s%n", new Double(0.001*(System.currentTimeMillis()-T0)), Thread.currentThread().getName(), " : "+msg);
}
public static void main(String[] args) throws Exception {
ForkJoinPool p = new ForkJoinPool(
NTHREADS,
pool -> {
int id = nextId.incrementAndGet(); //count new threads
log("new FJ thread "+ id);
ForkJoinWorkerThread t = new ForkJoinWorkerThread(pool) {/**/};
t.setName("My FJThread "+id);
return t;
},
Thread.getDefaultUncaughtExceptionHandler(),
false
);
LowercasingTask t = new LowercasingTask("ROOT", 3);
p.invoke(t);
int nt = nextId.get();
log("number of threads was "+nt);
if(nt > NTHREADS)
log(">>>>>>> more threads than prescribed <<<<<<<<");
}
//=====================
static class LowercasingTask extends RecursiveTask<String> {
String name;
int level;
public LowercasingTask(String name, int level) {
this.name = name;
this.level = level;
}
#Override
protected String compute() {
StringBuilder sbtl = myTL.get();
String initialValue = sbtl.toString();
if(!initialValue.equals(""))
log("!!!!!! BROKEN ON START!!!!!!! value = "+ initialValue);
sbtl.append(":START");
if(level>0) {
log(name+": compute level "+level);
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
List<LowercasingTask> tasks = new ArrayList<>();
for(int i=1; i<=9; i++) {
LowercasingTask lt = new LowercasingTask(name+"."+i, level-1);
tasks.add(lt);
lt.fork();
}
for(int i=0; i<tasks.size(); i++) { //this can lead to compensation threads due to l1.join() method running lifo task lN
//for(int i=tasks.size()-1; i>=0; i--) { //this usually has the lN.join() method running task lN, without compensation threads.
tasks.get(i).join();
}
log(name+": returning from joins");
}
sbtl.append(":END");
String val = sbtl.toString();
if(!val.equals(":START:END"))
log("!!!!!! BROKEN AT END !!!!!!! value = "+val);
sbtl.setLength(0);
return "done";
}
}
}
I don't believe so. Not in general and specially not for the ForkJoinTask where tasks are expected to be pure functions on isolated objects.
Sometimes it is possible to change the order of the task to fork and join at the beginning and before the own task's work. That way the subtask will initialize and dispose the thread-local before returning. If that is not possible, maybe you can treat the thread-local as a stack and push, clear, and restore the value around each join.
Related
Let's take this simple class:
public class CounterService {
private volatile Counter counter;
public CounterService(Counter counter) {
this.counter = counter;
}
public long getCounterValue() {
System.out.println("GET: " + this.counter.counter + " in thread " +
Thread.currentThread().getName());
return this.counter.counter;
}
public long setCounterValue(long newValue) {
this.counter = this.counter.updateCounter(newValue);
System.out.println("--set: " + newValue + " in thread " +
Thread.currentThread().getName());
return this.counter.counter;
}
}
public class Counter {
public final long counter;
public Counter(long counter) {
this.counter = counter;
}
public Counter updateCounter(long i) {
return new Counter(i);
}
}
Now I want to write the unit test, that will always pass, if the CounterService is thread safe (eg. when I set get and set methods synchronized). I belive that writing a test, which will always fail if this class isn't thread safe, may be impossible.
I tried with something like this:
#Test
public void multipleThreadSetAndGetShouldCorrectValue() throws ExecutionException, InterruptedException {
int threads = 10;
final Counter counter = new Counter(0);
final CounterService counterService = new CounterService(counter);
CountDownLatch latch = new CountDownLatch(1);
ExecutorService executorService = Executors.newFixedThreadPool(threads);
Collection<Future<Long>> results = new ArrayList<>();
AtomicLong sequence = new AtomicLong(0);
for (int i = 0; i < threads; i++) {
results.add(executorService.submit(() -> {
latch.await(1, TimeUnit.SECONDS);
latch.countDown();
counterService.setCounterValue(sequence.getAndIncrement());
return counterService.getCounterValue();
}));
}
final Set<Long> uniqueResult = new HashSet<>();
for (Future<Long> result : results) {
uniqueResult.add(result.get());
}
assertEquals(threads, uniqueResult.size());
}
But this test will occasionally fail even if the CounterService is thread safe.
How to write unit test that will always pass when the class is thread safe? How to write test to check, that get method returns the last set value, even if it was modified by another thread?
First, your Counter class is pointless. The updateCounter method doesn't update, it returns a new object. So just delete the Counter class and use long in your CounterService.
There then remains the question as to what CounterService is for. It just wraps a long.
But disregarding that. No - you can't really write a test to prove that something is not thread-safe as multi-threading problems are not deterministic. You could insert delays into places where you know race conditions might occur, but that only works if you already know it isn't thread-safe in a particular place and you want to prove it. But if you don't know where the problem is you might not be able to insert the delays in the right place to prove the possible problem does exist.
By the same token, you can't really prove that it is correct either, although again you can increase your chances by inserting sleeps between operations to force problems. But it may not work and you are not testing the real-world scenario.
Your test is failing because you don't understand what synchronized does and what thread-safely implies.
In your test, you are setting a counter value and then getting it in the next line. If you synchronize the set and the get, all that means is that the individual get and set operations are thread-safe. It doesn't mean that you can call get and set separately and that get will return the same value that the previous set used.
If you want to set something and then get the same value back again safely you have to wrap the get and the set calls in a synchronized block.
synchonized(this) { // get must return same thing that was set
set
get
}
I strongly recommend that you focus on understanding what your program needs to achieve and what thread-safety and sychronization means in that context. Otherwise, you won't be able to develop a correct test anyway.
I have a problem in Java where I want to spawn multiple concurrent threads simultaneously. I want to use the result of whichever thread/task finishes first, and abandon/ignore the results of the other threads/tasks. I found a similar question for just cancelling slower threads but thought that this new question was different enough to warrant an entirely new question.
Note that I have included an answer below based what I considered to be the best answer from this similar question but changed it to best fit this new (albeit similar) problem. I wanted to share the knowledge and see if there is a better way of solving this problem, hence the question and self-answer below.
You can use ExecutorService.invokeAny. From its documentation:
Executes the given tasks, returning the result of one that has completed successfully …. Upon normal or exceptional return, tasks that have not completed are cancelled.
This answer is based off #lreeder's answer to the question "Java threads - close other threads when first thread completes".
Basically, the difference between my answer and his answer is that he closes the threads via a Semaphore and I just record the result of the fastest thread via an AtomicReference. Note that in my code, I do something a little weird. Namely, I use an instance of AtomicReference<Integer> instead of the simpler AtomicInteger. I do this so that I can compare and set the value to a null integer; I can't use null integers with AtomicInteger. This allows me to set any integer, not just a set of integers, excluding some sentinel value. Also, there are a few less important details like the use of an ExecutorService instead of explicit threads, and the changing of how Worker.completed is set, because previously it was possible that more than one thread could finish first.
public class ThreadController {
public static void main(String[] args) throws Exception {
new ThreadController().threadController();
}
public void threadController() throws Exception {
int numWorkers = 100;
List<Worker> workerList = new ArrayList<>(numWorkers);
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(1);
//Semaphore prevents only one thread from completing
//before they are counted
AtomicReference<Integer> firstInt = new AtomicReference<Integer>();
ExecutorService execSvc = Executors.newFixedThreadPool(numWorkers);
for (int i = 0; i < numWorkers; i++) {
Worker worker = new Worker(i, startSignal, doneSignal, firstInt);
execSvc.submit(worker);
workerList.add(worker);
}
//tell workers they can start
startSignal.countDown();
//wait for one thread to complete.
doneSignal.await();
//Look at all workers and find which one is done
for (int i = 0; i < numWorkers; i++) {
if (workerList.get(i).isCompleted()) {
System.out.printf("Thread %d finished first, firstInt=%d\n", i, firstInt.get());
}
}
}
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
// null when not yet set, not so for AtomicInteger
private final AtomicReference<Integer> singleResult;
private final int id;
private boolean completed = false;
public Worker(int id, CountDownLatch startSignal, CountDownLatch doneSignal, AtomicReference<Integer> singleResult) {
this.id = id;
this.startSignal = startSignal;
this.doneSignal = doneSignal;
this.singleResult = singleResult;
}
public boolean isCompleted() {
return completed;
}
#Override
public void run() {
try {
//block until controller counts down the latch
startSignal.await();
//simulate real work
Thread.sleep((long) (Math.random() * 1000));
//try to get the semaphore. Since there is only
//one permit, the first worker to finish gets it,
//and the rest will block.
boolean finishedFirst = singleResult.compareAndSet(null, id);
// only set this if the result was successfully set
if (finishedFirst) {
//Use a completed flag instead of Thread.isAlive because
//even though countDown is the last thing in the run method,
//the run method may not have before the time the
//controlling thread can check isAlive status
completed = true;
}
}
catch (InterruptedException e) {
//don't care about this
}
//tell controller we are finished, if already there, do nothing
doneSignal.countDown();
}
}
This is a java concurrency question. 10 jobs need to be done, each of them will have 32 worker threads. Worker thread will increase a counter . Once the counter is 32, it means this job is done and then clean up counter map. From the console output, I expect that 10 "done" will be output, pool size is 0 and counterThread size is 0.
The issues are :
most of time, "pool size: 0 and countThreadMap size:3" will be
printed out. even those all threads are gone, but 3 jobs are not
finished yet.
some time, I can see nullpointerexception in line 27. I have used ConcurrentHashMap and AtomicLong, why still have concurrency
exception.
Thanks
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicLong;
public class Test {
final ConcurrentHashMap<Long, AtomicLong[]> countThreadMap = new ConcurrentHashMap<Long, AtomicLong[]>();
final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
final ThreadPoolExecutor tPoolExecutor = ((ThreadPoolExecutor) cachedThreadPool);
public void doJob(final Long batchIterationTime) {
for (int i = 0; i < 32; i++) {
Thread workerThread = new Thread(new Runnable() {
#Override
public void run() {
if (countThreadMap.get(batchIterationTime) == null) {
AtomicLong[] atomicThreadCountArr = new AtomicLong[2];
atomicThreadCountArr[0] = new AtomicLong(1);
atomicThreadCountArr[1] = new AtomicLong(System.currentTimeMillis()); //start up time
countThreadMap.put(batchIterationTime, atomicThreadCountArr);
} else {
AtomicLong[] atomicThreadCountArr = countThreadMap.get(batchIterationTime);
atomicThreadCountArr[0].getAndAdd(1);
countThreadMap.put(batchIterationTime, atomicThreadCountArr);
}
if (countThreadMap.get(batchIterationTime)[0].get() == 32) {
System.out.println("done");
countThreadMap.remove(batchIterationTime);
}
}
});
tPoolExecutor.execute(workerThread);
}
}
public void report(){
while(tPoolExecutor.getActiveCount() != 0){
//
}
System.out.println("pool size: "+ tPoolExecutor.getActiveCount() + " and countThreadMap size:"+countThreadMap.size());
}
public static void main(String[] args) throws Exception {
Test test = new Test();
for (int i = 0; i < 10; i++) {
Long batchIterationTime = System.currentTimeMillis();
test.doJob(batchIterationTime);
}
test.report();
System.out.println("All Jobs are done");
}
}
Let’s dig through all the mistakes of thread related programming, one man can make:
Thread workerThread = new Thread(new Runnable() {
…
tPoolExecutor.execute(workerThread);
You create a Thread but don’t start it but submit it to an executor. It’s a historical mistake of the Java API to let Thread implement Runnable for no good reason. Now, every developer should be aware, that there is no reason to treat a Thread as a Runnable. If you don’t want to start a thread manually, don’t create a Thread. Just create the Runnable and pass it to execute or submit.
I want to emphasize the latter as it returns a Future which gives you for free what you are attempting to implement: the information when a task has been finished. It’s even easier when using invokeAll which will submit a bunch of Callables and return when all are done. Since you didn’t tell us anything about your actual task, it’s not clear whether you can let your tasks simply implement Callable (may return null) instead of Runnable.
If you can’t use Callables or don’t want to wait immediately on submission, you have to remember the returned Futures and query them at a later time:
static final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
public static List<Future<?>> doJob(final Long batchIterationTime) {
final Random r=new Random();
List<Future<?>> list=new ArrayList<>(32);
for (int i = 0; i < 32; i++) {
Runnable job=new Runnable() {
public void run() {
// pretend to do something
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(r.nextInt(10)));
}
};
list.add(cachedThreadPool.submit(job));
}
return list;
}
public static void main(String[] args) throws Exception {
Test test = new Test();
Map<Long,List<Future<?>>> map=new HashMap<>();
for (int i = 0; i < 10; i++) {
Long batchIterationTime = System.currentTimeMillis();
while(map.containsKey(batchIterationTime))
batchIterationTime++;
map.put(batchIterationTime,doJob(batchIterationTime));
}
// print some statistics, if you really need
int overAllDone=0, overallPending=0;
for(Map.Entry<Long,List<Future<?>>> e: map.entrySet()) {
int done=0, pending=0;
for(Future<?> f: e.getValue()) {
if(f.isDone()) done++;
else pending++;
}
System.out.println(e.getKey()+"\t"+done+" done, "+pending+" pending");
overAllDone+=done;
overallPending+=pending;
}
System.out.println("Total\t"+overAllDone+" done, "+overallPending+" pending");
// wait for the completion of all jobs
for(List<Future<?>> l: map.values())
for(Future<?> f: l)
f.get();
System.out.println("All Jobs are done");
}
But note that if you don’t need the ExecutorService for subsequent tasks, it’s much easier to wait for all jobs to complete:
cachedThreadPool.shutdown();
cachedThreadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
System.out.println("All Jobs are done");
But regardless of how unnecessary the manual tracking of the job status is, let’s delve into your attempt, so you may avoid the mistakes in the future:
if (countThreadMap.get(batchIterationTime) == null) {
The ConcurrentMap is thread safe, but this does not turn your concurrent code into sequential one (that would render multi-threading useless). The above line might be processed by up to all 32 threads at the same time, all finding that the key does not exist yet so possibly more than one thread will then be going to put the initial value into the map.
AtomicLong[] atomicThreadCountArr = new AtomicLong[2];
atomicThreadCountArr[0] = new AtomicLong(1);
atomicThreadCountArr[1] = new AtomicLong(System.currentTimeMillis());
countThreadMap.put(batchIterationTime, atomicThreadCountArr);
That’s why this is called the “check-then-act” anti-pattern. If more than one thread is going to process that code, they all will put their new value, being confident that this was the right thing as they have checked the initial condition before acting but for all but one thread the condition has changed when acting and they are overwriting the value of a previous put operation.
} else {
AtomicLong[] atomicThreadCountArr = countThreadMap.get(batchIterationTime);
atomicThreadCountArr[0].getAndAdd(1);
countThreadMap.put(batchIterationTime, atomicThreadCountArr);
Since you are modifying the AtomicInteger which is already stored into the map, the put operation is useless, it will put the very array that it retrieved before. If there wasn’t the mistake that there can be multiple initial values as described above, the put operation had no effect.
}
if (countThreadMap.get(batchIterationTime)[0].get() == 32) {
Again, the use of a ConcurrentMap doesn’t turn the multi-threaded code into sequential code. While it is clear that the only last thread will update the atomic integer to 32 (when the initial race condition doesn’t materialize), it is not guaranteed that all other threads have already passed this if statement. Therefore more than one, up to all threads can still be at this point of execution and see the value of 32. Or…
System.out.println("done");
countThreadMap.remove(batchIterationTime);
One of the threads which have seen the 32 value might execute this remove operation. At this point, there might be still threads not having executed the above if statement, now not seeing the value 32 but producing a NullPointerException as the array supposed to contain the AtomicInteger is not in the map anymore. This is what happens, occasionally…
After creating your 10 jobs, your main thread is still running - it doesn't wait for your jobs to complete before it calls report on the test. You try to overcome this with the while loop, but tPoolExecutor.getActiveCount() is potentially coming out as 0 before the workerThread is executed, and then the countThreadMap.size() is happening after the threads were added to your HashMap.
There are a number of ways to fix this - but I will let another answer-er do that because I have to leave at the moment.
I've read through the API documentation of the java.util.concurrent package, but have obviously misunderstood something. The overview says
A small toolkit of classes that support lock-free thread-safe
programming on single variables.
However, a small test application shows that the AtomicInteger class does not provide thread-safety, at least when it is shared across threads (I accept that the getAndSet / increment methods themselves are at least atomic)
Test:
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntTest
{
public static void main(String[] args) throws InterruptedException
{
AtomicInteger atomicInt = new AtomicInteger(0);
WorkerThread w1 = new WorkerThread(atomicInt);
WorkerThread w2 = new WorkerThread(atomicInt);
w1.start();
w2.start();
w2.join(); // <-- As pointed out by StuartLC and BarrySW19, this should be w1.join(). This typo allows the program to produce variable results because it does not correctly wait for *both* threads to finish before outputting a result.
w2.join();
System.out.println("Final value: " + atomicInt.get());
}
public static class WorkerThread extends Thread
{
private AtomicInteger atomicInt = null;
private Random random = new Random();
public WorkerThread(AtomicInteger atomicInt)
{
this.atomicInt = atomicInt;
}
#Override
public void run()
{
for (int i = 0; i < 500; i++)
{
this.atomicInt.incrementAndGet();
try
{
Thread.sleep(this.random.nextInt(50));
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
When I run this class, I consistently get results ranging from around 950 to 1000, when I would expect to always see exactly 1000.
Can you explain why do I not get consistent results when two threads access this shared AtomicInteger variable? Have I misunderstood the thread-safety guarantee?
Looks like a simple cut&paste error - you are joining to thread "w2" twice and never to "w1". At present, you would expect the thread "w1" to still be running half the time when you print the 'final' value.
Let's say i have CPU with 2 cores. If i will run background processing service with Executors.newFixedThreadPool(4) threads am i correct that:
during lifetime of executor service single thread could be running on different cores
even with no synchronization if thread A runs its code on core 1 and left in CPU cache some value of shared singleton let's say, and then if thread B will run it's code on same core and will try get singleton value from same memory location which has representation left by thread A in cache - it will get it from CPU core L1 or L2 cache. And if thread B will use synchronization it will read new value from main memory(latest version). In general if some thread left in CPU core cache some value of private field of shared object - another thread that could be run on same core could see value of private member from cache left by other thread.
If both options on top will be true - if L2 cache will be used to store shared between threads(which will add new values to map) HashMap instance and L2 will be a shared between all cores cache - does it mean that while skipping not atomic operations(if we want just to see correct/latest values in map) we can skip synchronization. For example will it be correct to have a HashMap and skip synchronization on reading existing values from Map:
Example
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Launcher {
public static void main(String[] args) throws Exception {
final Stats stats = new Stats();
final Random key = new Random();
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(new Runnable() {
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
String keyValue = String.valueOf(key.nextInt(10));
int value = stats.inc(keyValue);
System.out.println("[A] Key " + keyValue + " was incremented to " + value);
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
service.submit(new Runnable() {
#Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
int[] values = new int[10];
for (int i = 0; i< 10; i++) {
values[i] = stats.get(String.valueOf(i));
}
System.out.println("[B] " + Arrays.toString(values));
try {
TimeUnit.MILLISECONDS.sleep(1500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
}
static class Stats {
private final Map<String, Number> statistics = new HashMap<String, Number>();
public int inc(String key) {
if (!statistics.containsKey(key)) {
synchronized (statistics) {
statistics.put(key, new AtomicInteger(0));
}
}
return ((AtomicInteger) statistics.get(key)).getAndIncrement();
}
public int get(String key) {
if (!statistics.containsKey(key)) {
return 0;
}
return statistics.get(key).intValue();
}
}
}
Could you point me to some valuable documentation of low level management of multithreaded code in java?
Guys i really understand that we should not rely on specific architecture/CPU/ etc. I'm just curious if probability of described points bigger than 0 :)
Thx in advance
You shouldn't make any assumptions about threads seeing values modified by other threads unless you synchronize on the access or make the variables volatile.
Any other behaviour is unreliable and subject to change.
Remember that Java is running on the JVM, not directly on your processor, and has license to make a LOT of optimisations to your running code. So while a lot of the behaviour carries over you cannot rely upon it. Especially since as soon as you run on different architecture or under different conditions the exact same bytecode may be optimised differently.