Java: Thread joins over 10,000 iterations inconsistent - java

Alright folks.. I'm back again (seems to be my home lately).
I'm going through the whole cave of programming YouTube vids on multi-threading. This particular one uses 2 threads that go through a for loop which adds 1 to a variable 10,000 times each. So you join them so the result is 20,000 when it's done.
public class main {
private int count = 0;
public static void main(String[] args) {
main main = new main();
main.doWork();
}
public void doWork(){
Thread t1 = new Thread(new Runnable(){
public void run(){
for (int i = 0; i < 10000; i++){
count++;
}
}
});
Thread t2 = new Thread(new Runnable(){
public void run(){
for (int i = 0; i < 10000; i++){
count++;
}
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException ex) {
Logger.getLogger(main.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Count is: " + count);
}
}
Thing is.. when i change the iterations:
i < 10 = 20 (correct)
i < 100 = 200 (correct)
i < 1000 = 2000 (correct)
i < 10000 = 13034 (first run)
= 14516 (second run)
= ... etc..
Why won't it properly handle iterations in the tens of thousands?

You have demonstrated the classic race condition, which occurs when 2 or more threads are reading and writing to the same variable in conflicting ways. This arises because the ++ operator isn't an atomic operation -- multiple operations are occurring, and a thread could be interrupted in between operations, e.g.:
Thread t1 reads count (0), and calculates the incremented value (1), but it hasn't stored the value back to count yet.
Thread t2 reads count (still 0), and calculates the incremented value (1), but it hasn't stored the value back to count yet.
Thread t1 stores its 1 value back to count.
Thread t2 stores its 1 value back to count.
Two updates have occurred, but the net result is only an increase of 1. The set of operations which must not be interrupted is a critical section.
This appears to have happened 20,000 - 13,034 times, or 6,966 times in your first execution. You may have gotten lucky with lower bounds, but regardless of the magnitude of the bounds, the race condition can happen.
In Java, there are several solutions:
Place synchronized blocks around the critical sections (both count++ lines), locking on this.
Change count to an AtomicInteger, which encapsulates such operations atomically on its own. The getAndIncrement method would replace the ++ operator here.

Related

How does polling with thread.sleep() work?

I am new to threads in Java, I am trying to understand how they work; considering the following code:
public class ZooInfo {
private static int counter = 0;
public static void main(String args[]) throws InterruptedException {
new Thread(() -> {
for(int i=0; i<500; i++) ZooInfo.counter++;
}).start();
while(ZooInfo.counter<100) {
System.out.println("Not reached yet");
Thread.sleep(1000);
}
System.out.println("Reached!");
}
}
The output of the above code:
Not reached yet
Reached!
I don't understand why the output prints "Not reached yet" just once, and not for every time counter is less than a 100. I was expecting the output to print "Not reached yet" a 100 then print "Reached"?
"I don't understand why the output prints "Not reached yet" just once, and not for every time counter is less than a 100." - It does! The value of ZooInfo.counter, however, is only checked once a second. The thread started (new Thread(() -> { for(int i=0; i<500; i++) ZooInfo.counter++; }).start(); will take significantly less than a second for the first 100 iterations. Removing the Thread.sleep(...) does not necessarily change the behaviour (for me, it leads to Not yet reached! being printend 1-3 times). This is due to the fact that the newly created thread still has a head start. Also, the two threads do not run "in unison", i.e. one thread may proceed faster or slower than the other.
Furthermore, as was pointed out by #Amongalen and #Polygnome, printing to System.out is slow. We can improve this by using a StringBuilder:
final String lineSeparator = System.lineSeparator();
new Thread(() -> {
for (int i = 0; i < 500; i++) ZooInfo.counter++;
}).start();
final StringBuilder builder = new StringBuilder();
while (ZooInfo.counter < 100) {
builder.append("Not reached yet").append(lineSeparator);
}
builder.append("Reached!");
System.out.println(builder.toString());
This will increase the number of times Not reached yet! is printed.
Ideone demo
A remark: the field private static int counter = 0; should also be declared volatile to guarantee proper visibility between threads.

Program not always terminating? [duplicate]

This question already has answers here:
Why is i++ not atomic?
(10 answers)
What is a debugger and how can it help me diagnose problems?
(2 answers)
Closed 4 years ago.
I wanted to test out multithreading for a project of mine, trying to also develop a solution in case something goes wrong.
So I made this small test:
main
public class main
{
static int addToCounter;
static int addToErrorCounter;
public static void main(String[] args) throws InterruptedException
{
int threads = 10;
Executor exec = new Executor();
for (int i = 0; i < threads; i++)
{
double error = Math.random();
testClass aldo = new testClass();
Thread thread = aldo.getThread(300, error);
exec.execute(thread);
}
while (threads != (addToCounter + addToErrorCounter))
{
System.out.println("Not all threads finished, number of finished threads is: " + (addToCounter + addToErrorCounter));
Thread.sleep(50);
}
System.out.println("Number of Threads that finished correctly: " + addToCounter);
}
}
testClass
import test1.main;
public class testClass
{
public Thread getThread(long time, double error)
{
Thread thread = new Thread()
{
public void run()
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
if (error > 0.5)
{
main.addToErrorCounter++;
throw new java.lang.Error("HELLO");
}
System.out.println("I DID THIS!");
main.addToCounter++;
}
};
return thread;
}
}
(you'll have to fix the imports, also I use a custom class Executor, although that's only a wrapper for ExecutorService)
The weird behaviour is that sometimes it works properly, and sometimes it doesn't (total terminated thread count is 9, although I can see clearly it printed "I DID THIS!" and the error exactly 10 times).
Any fix?
The Problem might be a racecondition.
the "++" operator is not atomic.
Imageine the following scenario. There are two Threads at the same time. both want to increase a number and finish.
The initial value of the number is 0.
Thread 0 reads the number, knows now it is 0.
Thread 1 reads the number, knows now it is 0.
Thread 0 (who knew it was 0) now writes 1 to the memory.
Thread 1 does not know, that the number has changed, and still believes the number is 0 so he also writes a 1 to the memory.
You need something like a synchronizing mechanisim, something like a lock, or a semaphore or something else.
have a look at this for more information: http://winterbe.com/posts/2015/04/30/java8-concurrency-tutorial-synchronized-locks-examples/
for your example you could use the "synchronized" example from that link.
add a method to your main class looking like this to increment the addToCounter and also to the addToErrorCounterto remove the effects from your error counter:
synchronized AddToError(int e){
addToError += e;
}
synchronized IncCounter(){
addToCounter++;
}
call those methods in your threads in the testclass instead of incrementing them unsynchronized.
My guess is that the postfix operator (main.addToCounter++) is not atomic. This line of code is probably equivalent to something like:
int temp = main.addToCounter;
main.addToCounter = temp + 1;
return temp;
With multiple threads doin this at the same time, two threads could obtain the same value for temp (because both peform the first line in the above pseudo-code before either performs the second), and hence the counter total will be too small once all threads are complete. See Why is i++ not atomic? for more information.
A quick fix in this situation is to make addToCounter an AtomicInteger, then use addToCounter.incrementAndGet() in place of addToCounter++.

Java Multi-threading : Unexpected result

I am working on a Enterprise application. I am facing some issues while running application in multithreaded environment. I am writing a program in which there is a variable whose value is getting updated(incremented) at very fast rate (for example 10000 updates/persecond). A loop runs for certain iterations and the value of the variable is incremented and stored in HashMap. Once the loop terminates and value the variable in HashMap is printed. I am getting unexpected value of the variable.
Here is demo program (Please read comments for better understanding) :
class test implements Runnable {
static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
static AtomicInteger value_to_be_incremented_stored = new AtomicInteger(0); // variable whose value to be updated
static AtomicInteger i = new AtomicInteger(0); // this runs the loop
#Override
public void run() {
for (i.set(0); i.get() < 100000; i.incrementAndGet()) {
/*
This loop should run 100000 times and when loop terminates according to me value of variable
"value_to_be_incremented_stored" should be 100000 as its value is incremented
100000 times the loop also runs 100000 times.
*/
System.out.println("Thread > " + Thread.currentThread() + " " + value_to_be_incremented_stored.incrementAndGet());
map.put("TC", value_to_be_incremented_stored.intValue());
}
System.out.println("Output by Thread " + Thread.currentThread() + " " + map.toString());
}
public static void main(String[] args) {
test t1 = new test();
Thread thread1 = new Thread(t1);
thread1.setName("Thread 1");
Thread thread2 = new Thread(t1);
thread2.setName("Thread 2");
Thread thread3 = new Thread(t1);
thread3.setName("Thread 3");
Thread thread4 = new Thread(t1);
thread4.setName("Thread 4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
Output (it varies) :
Issue :
I am running loop for 100000 times (i.get() < 100000) then how come value of variable value_to_be_incremented_stored becomes more than 100000.
I found three defects. One there is a race condition in the for loop between the point where you compare the loop counter, and where you increment it. You should do this in one step to get an atomic operation:
for ( ; i.incrementAndGet() < 100000; ) {
The other is there is also a race condition between the increment of your counter, and placing it in the map. Even though you increment these in series, any thread could be have a different value internally (it's at a different point in the loop) and it could put a previous value in the global map. You need atomicity here to to make sure the value you increment is the value you place in the loop.
synchronized( lock ) {
value_to_be_incremented_stored.incrementAndGet();
map.put("TC", value_to_be_incremented_stored.intValue());
}
Finally for some reason the < comparison produces a value of 99999 for me. I had to use <= to fix it.
(And as we discussed in the comments, setting i.set(0) at the start of each for loop doesn't work for fairly obvious reasons. Four defects, I guess.)
class ThreadTestX implements Runnable {
static ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
static AtomicInteger value_to_be_incremented_stored = new AtomicInteger(0); // variable whose value to be updated
static AtomicInteger i = new AtomicInteger(0); // this runs the loop
static final Object lock = new Object();
#Override
public void run() {
for ( ; i.incrementAndGet() <= 100000; ) {
/*
This loop should run 100000 times and when loop terminates according to me value of variable
"value_to_be_incremented_stored" should be 100000 as its value is incremented
100000 times the loop also runs 100000 times.
*/
synchronized( lock ) {
value_to_be_incremented_stored.incrementAndGet();
// System.out.println("Thread > " + Thread.currentThread() +
// " " + value_to_be_incremented_stored.get());
map.put("TC", value_to_be_incremented_stored.intValue());
}
}
System.out.println("Output by Thread " + Thread.currentThread()
+ " " + map.toString());
}
public static void main(String[] args) {
ThreadTestX t1 = new ThreadTestX();
Thread thread1 = new Thread(t1);
thread1.setName("Thread 1");
Thread thread2 = new Thread(t1);
thread2.setName("Thread 2");
Thread thread3 = new Thread(t1);
thread3.setName("Thread 3");
Thread thread4 = new Thread(t1);
thread4.setName("Thread 4");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
Output:
run:
Output by Thread Thread[Thread 4,5,main] {TC=100000}
Output by Thread Thread[Thread 3,5,main] {TC=100000}
Output by Thread Thread[Thread 1,5,main] {TC=100000}
Output by Thread Thread[Thread 2,5,main] {TC=100000}
BUILD SUCCESSFUL (total time: 0 seconds)
Afterthoughts: And in spite of getting marked correct, I'm not sure I was correct. The problem here is that you are trying to keep three things in sync: the loop counter i, the value to be incremented, and the map. Allowing any of these to be executed outside of a synchronized block may invite them to be in an unexpected state. I think the following may be safer:
#Override
public void run() {
for ( ;; ) {
synchronized( lock ) {
if( i.incrementAndGet() <= 100000 ) {
value_to_be_incremented_stored.incrementAndGet();
map.put("TC", value_to_be_incremented_stored.intValue());
}
else
break;
}
}
System.out.println("Output by Thread " + Thread.currentThread()
+ " " + map.toString());
}
This removes the need for declaring the variables as AtomicInteger, but I don't see how else you ensure that their values don't change (due to some other thread) as that loop executes.
Your "demo program" suffers from two simultaneous defects.
Defect #1 is the i.set( 0 ) pointed out by tsolakp. You say you fixed that but you are still getting a value of more than 100000. I also tried it and indeed, the final value is still larger than 100000.
I modified the program to be able to create an arbitrary number of threads, and I tried with 3, 10, and 20 threads. I got a final number of 100003, 100009, and 100019 respectively. See a pattern? So:
Defect #2 is that on the last iteration, when the value of i is 99999, the expression i.get() < 100000 is true for all threads, so all threads proceed to execute once more. The i.incrementAndGet() clause is visually sitting right next to i.get() < 1000000; but it does not get executed until the end of the loop.
So, all threads get a chance to increment i once more after the last iteration.
Everytime a new thread enters the run method it will reset your i count to zero via the first statement in the for loop by calling i.set(0).
Update: Next step after fixing reset issue, is to step through the code and see how the threads will behave.
Lets say three threads get inside the for loop while the fourth thread increments i. What will happen is that value_to_be_incremented_stored will increment 3 times and i only one time.

Data Races in an AtomicIntegerArray

In the code below:
I am updating num[1]=0 of an AtomicIntegerArray num 1000 times each in 2 threads.
At the end of the 2 threads in main thread ;shouldn't the value of num[1] be 2000 as there shouldn't be data races in an AtomicIntegerArray .
However I get random values < 2000. Could someone tell me why?
Code:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArr {
private static AtomicIntegerArray num= new AtomicIntegerArray(2);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MyRun1());
Thread t2 = new Thread(new MyRun2());
num.set(0, 10);
num.set(1, 0);
System.out.println("In Main num before:"+num.get(1));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("In Main num after:"+num.get(1));
}
static class MyRun1 implements Runnable {
public void run() {
for (int i = 0; i < 1000; i++) {
num.set(1,num.get(1)+1);
}
}
}
static class MyRun2 implements Runnable {
public void run() {
for (int i = 0; i < 1000; i++) {
num.set(1,num.get(1)+1);
}
}
}
}
Edit: Adding num.compareAndSet(1, num.get(1), num.get(1)+1); instead of num.set(1,num.get(1)+1); doesnt work either.
I get random values < 2000. Could someone tell me why?
This is called the lost-update problem.
Because, in the following code:
num.set(1, num.get(1) + 1);
Although each individual operation involved is atomic, the combined operation is not. The single operations from the two threads can interleave, causing updates from one thread to be overwritten with stale value by another thread.
You can use compareAndSet to solve this problem, but you have to check whether the operation is successful, and do it again when it fails.
int v;
do {
v = num.get(1);
} while (!num.compareAndSet(1, v, v+1));
There's also a method for exactly this purpose:
num.accumulateAndGet(1, 1, (x, d)->x+d);
accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction)
Atomically updates the element at index i with the results of applying the given function to the current and given values, returning the updated value. The function should be side-effect-free, since it may be re-applied when attempted updates fail due to contention among threads. The function is applied with the current value at index i as its first argument, and the given update as the second argument.
This is a classic race condition. Any time you have a fetch, an operation, and a put, your code is racy.
Consider two threads, both executing num.set(1,num.get(1)+1) at roughly the "same time." First, let's break down what the expression itself is doing:
it fetches num.get(1); let's call this x
it adds 1 to that; let's call this y
it puts that sum in at `num.set(1, y);
Even though the intermediate values in your expression are just values on the stack, and not explicit variables, the operation is the same: get, add, put.
Okay, so back to our two threads. What if the operations are ordered like this?
inital state: n[1] = 5
Thread A | Thread B
========================
x = n[1] = 5 |
| x = n[1] = 5
| y = 5 + 1 = 6
y = 5 + 1 = 6 |
n[1] = 6 |
| n[1] = 6
Since both threads fetched the value before either thread put its added value, they both do the same thing. You have 5 + 1 twice, and the result is 6, not 7!
What you want is getAndIncrement(int idx), or one of the similar methods that does the get, adding, and putting atomically.
These methods can actually all be built on top of the compareAndSet method you identified. But to do that, you need to do the increment within a loop, trying until the compareAndSet returns true. Also, for that to work, you have store that initial num.get(1) value in a local variable, rather than fetching it a second time. In effect, this loop says "keep trying the get-add-put logic until it works without anyone else having raced between the operations." In my example above, Thread B would have noticed that compareAndSet(1, 5, 6) fails (since the actual value at that time is 6, not 5 as expected), and thus retried. This is in fact what all of those atomic methods, like getAndIncrement, do.

Java Synchronized Method and Block

I'm trying to understand synchronization of multiple threads in Java more fully. I understand the high level idea behind the use of the synchronized keyword, and how it provides mutual exclusion among threads.
The only thing is that most of the examples I read online and in my textbook still work correctly even if you remove the synchronized keyword which is making this topic more confusing than I think it needs to be.
Can anyone provide me with a concrete example of when not including the synchronized keyword will produce erroneous results? Any information would be much appreciated.
You can usually trigger a race condition by increasing the number of iterations. Here's a simple example that works with 100 and 1,000 iterations but fails (at least on my quad-core box) at 10,000 iterations (sometimes).
public class Race
{
static final int ITERATIONS = 10000;
static int counter;
public static void main(String[] args) throws InterruptedException {
System.out.println("start");
Thread first = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < ITERATIONS; i++) {
counter++;
}
}
});
Thread second = new Thread(new Runnable() {
#Override
public void run() {
for (int i = 0; i < ITERATIONS; i++) {
counter++;
}
}
});
first.start();
second.start();
first.join();
second.join();
System.out.println("Counter " + counter + " should be " + (2 * ITERATIONS));
}
}
>>> Counter 12325 should be 20000
This example fails because access to counter is not properly synchronized. It can fail in two ways, possibly both in the same run:
One thread fails to see that the other has incremented the counter because it doesn't see the new value.
One thread increments the counter between the other thread reading the current value and writing the new value. This is because the increment and decrement operators are not atomic.
The fix for this simple program would be to use an AtomicInteger. Using volatile isn't enough due to the problem with increment, but AtomicInteger provides atomic operations for increment, get-and-set, etc.
The thing about race conditions is that they don't necessarily happen if you don't do proper synchronization -- indeed, quite frequently it'll work just fine -- but then one year later, in the middle of the night, your code will crash with a completely unpredictable bug that you can't reproduce, because the bug only appears randomly.
Race conditions are so insidious precisely because they don't always make your program crash, and they trigger more or less randomly.

Categories

Resources