From page 291 of OCP Java SE 6 Programmer Practice Exams, question 25:
public class Stone implements Runnable {
static int id = 1;
public void run() {
id = 1 - id;
if (id == 0)
pick();
else
release();
}
private static synchronized void pick() {
System.out.print("P ");
System.out.print("Q ");
}
private synchronized void release() {
System.out.print("R ");
System.out.print("S ");
}
public static void main(String[] args) {
Stone st = new Stone();
new Thread(st).start();
new Thread(st).start();
}
}
One of the answers is:
The output could be P Q P Q
I marked this answer as correct. My reasoning:
We are starting two threads.
First one enters run().
According to JLS 15.26.1, it firstly evaluates 1 - id. Result is 0. It is stored on the thread's stack. We are just about to save that 0 to static id, but...
Boom, scheduler chooses the second thread to run.
So, the second thread enters run(). Static id is still 1, so he executes method pick(). P Q is printed.
Scheduler chooses first thread to run. It takes 0 from its stack and saves to static id. So, the first thread also executes pick() and prints P Q.
However, in the book it's written that this answer is incorrect:
It is incorrect because the line id = 1 - id swaps the value of id between 0 and 1. There is no chance for the same method to be executed twice.
I don't agree. I think there is some chance for the scenario I presented above. Such swap is not atomic. Am I wrong?
Am I wrong?
Nope, you're absolutely right - as is your example timeline.
In addition to it not being atomic, it's not guaranteed that the write to id will be picked up by the other thread anyway, given that there's no synchronization and the field isn't volatile.
It's somewhat disconcerting for reference material like this to be incorrect :(
In my opinion, the answer in the Practice Exams is correct. In this code, you are executing two threads which have access to the same static variable id. Static variables are stored on the heap in java, not on the stack. The execution order of runnables is unpredictable.
However, in order to change the value of id each thread :
makes local copy of the value stored in id's memory address to the CPU registry;
performs the operation 1 - id. Strictly speaking, two operations are performed here (-id and +1);
moves the result back to memory space of id on the heap.
This means that although the id value can be changed concurrently by any of the two threads, only the initial and final values are mutable. Intermediate values will not be modified by one another.
Futhermore, analysis of the code can show that at any point in time, id can only be 0 or 1.
Proof:
Starting value id = 1;
One thread will change it to 0 ( id = 1 - id ). And the other thread will bring it back to 1.
Starting value id = 0;
One thread will change it to 1 ( id = 1 - id ). And the other thread will bring it back to 0.
Therefore, the value state of id is discrete either 0 or 1.
End of Proof.
There can be two possibilities for this code:
Possibility 1. Thread one accesses the variable id first. Then the value of id (id = 1 - id changes to 0. Thereafter, only the method pick () will be executed, printing P Q. Thread two, will evaluate id at that time id = 0; method release() will then be executed printing R S. As a result, P Q R S will be printed.
Possibility 2. Thread two accesses the variable id first. Then the value of id (id = 1 - id changes to 0. Thereafter, only the method pick () will be executed, printing P Q. Thread one, will evaluate id at that time id = 0; method release() will then be executed printing R S. As a result, P Q R S will be printed.
There are no other possibilities. However, it should be noted that variants of P Q R S such as P R Q S or R P Q S, etc. may be printed due to pick() being a static method and is therefore shared between the two threads. This leads to the simultaneous execution of this method which could result in printing the letters in a different order depending on your platform.
However in any case, never will either the method pick() or release () be executed twice as they are mutually exclusive. Therefore P Q P Q will not be an output.
Related
I'm working on a multithread application for an exercise used to simulate a warehouse (similar to the producer consumer problem) however I'm running into some trouble with the program where increasing the number of consumer threads makes the program behave in unexpected ways.
The code:
I'm creating a producer thread called buyer which has as a goal to order precisely 10 orders from the warehouse each. To do this they have a shared object called warehouse on which a buyer can place an order, the order is then stored in a buffer in the shared object. After this the buyer sleeps for some time until it either tries again or all packs have been bought. The code to do this looks like this:
public void run() {
//Run until the thread has bought 10 packages, this ensures the thread
//will eventually stop execution automatically.
while(this.packsBought < 10) {
try {
//Sleep for a random amount of time between 1 and 50
//milliseconds.
Thread.sleep(this.rand.nextInt(49) + 1);
//Catch any interruptExceptions.
} catch (InterruptedException ex) {
//There is no problem if this exception is thrown, the thread
//will just make an order earlier than planned. that being said
//there should be no manner in which this exception is thrown.
}
//Create a new order.
Order order = new Order(this.rand.nextInt(3)+ 1,
this,
this.isPrime);
//Set the time at which the order was placed as now.
order.setOrderTime(System.currentTimeMillis());
//place the newly created order in the warehouse.
this.warehouse.placeOrder(order);
}
//Notify the thread has finished execution.
System.out.println("Thread: " + super.getName() + " has finished.");
}
As you can see the function placeOrder(Order order); is used to place an order at the warehouse. this function is responsible for placing the order in the queue based on some logic related to prime status. The function looks like this:
public void placeOrder(Order order) {
try{
//halt untill there are enough packs to handle an order.
this.notFullBuffer.acquire();
//Lock to signify the start of the critical section.
this.mutexBuffer.lock();
//Insert the order in the buffer depending on prime status.
if (order.isPrime()) {
//prime order, insert behind all prime orders in buffer.
//Enumerate all non prime orders in the list.
for (int i = inPrime; i < sizeOrderList - 1; i++) {
//Move the non prime order back 1 position in the list.
buffer[i + 1] = buffer[i];
}
// Insert the prime order.
buffer[inPrime++] = order;
} else {
//No prime order, insert behind all orders in buffer.
buffer[inPrime + inNormal++] = order;
}
//Notify the DispatchWorkers that a new order has been placed.
this.notEmptyBuffer.release();
//Catch any InterruptException that might occure.
} catch(InterruptedException e){
//Even though this isn't expected behavior, there is no reason to
//notify the user of this event or to preform any other action as
//the thread will just return to the queue before placing another
//error if it is still required to do so.
} finally {
//Unlock and finalize the critical section.
mutexBuffer.unlock();
}
}
The orders are consumed by workers which act as the consumer thread. The thread itself contains very simple code looping until all orders have been processed. In this loop a different function handleOrder(); is called on the same warehouse object which handles a single order from the buffer. It does so with the following code:
public void handleOrder(){
//Create a variable to store the order being handled.
Order toHandle = null;
try{
//wait until there is an order to handle.
this.notEmptyBuffer.acquire();
//Lock to signify the start of the critical section.
this.mutexBuffer.lock();
//obtain the first order to handle as the first element of the buffer
toHandle = buffer[0];
//move all buffer elementst back by 1 position.
for(int i = 1; i < sizeOrderList; i++){
buffer[i - 1] = buffer[i];
}
//set the last element in the buffer to null
buffer[sizeOrderList - 1] = null;
//We have obtained an order from the buffer and now we can handle it.
if(toHandle != null) {
int nPacks = toHandle.getnPacks();
//wait until the appropriate resources are available.
this.hasBoxes.acquire(nPacks);
this.hasTape.acquire(nPacks * 50);
//Now we can handle the order (Simulated by sleeping. Although
//in real live Amazon workers also have about 5ms of time per
//package).
Thread.sleep(5 * nPacks);
//Calculate the total time this order took.
long time = System.currentTimeMillis() -
toHandle.getOrderTime();
//Update the total waiting time for the buyer.
toHandle.getBuyer().setWaitingTime(time +
toHandle.getBuyer().getWaitingTime());
//Check if the order to handle is prime or not.
if(toHandle.isPrime()) {
//Decrement the position of which prime orders are
//inserted into the buffer.
inPrime--;
} else {
//Decrement the position of which normal orders are
//inserted into the buffer.
inNormal--;
}
//Print a message informing the user a new order was completed.
System.out.println("An order has been completed for: "
+ toHandle.getBuyer().getName());
//Notify the buyer he has sucsessfully ordered a new package.
toHandle.getBuyer().setPacksBought(
toHandle.getBuyer().getPacksBought() + 1);
}else {
//Notify the user there was a critical error obtaining the
//error to handle. (There shouldn't exist a case where this
//should happen but you never know.)
System.err.println("Something went wrong obtaining an order.");
}
//Notify the buyers that a new spot has been opened in the buffer.
this.notFullBuffer.release();
//Catch any interrupt exceptions.
} catch(InterruptedException e){
//This is expected behavior as it allows us to force the thread to
//revaluate it's main running loop when notifying it to finish
//execution.
} finally {
//Check if the current thread is locking the buffer lock. This is
//done as in the case of an interrupt we don't want to execute this
//code if the thread interrupted doesn't hold the lock as that
//would result in an exception we don't want.
if (mutexBuffer.isHeldByCurrentThread())
//Unlock the buffer lock.
mutexBuffer.unlock();
}
}
The problem:
To verify the functionallity of the program I use the output from the statement:
System.out.println("An order has been completed for: "
+ toHandle.getBuyer().getName());
from the handleOrder(); function. I place the whole output in a text file, remove all the lines which aren't added by this println(); statement and count the number of lines to know how many orders have been handled. I expect this value to be equal to the amount of threads times 10, however this is often not the case. Running tests I've noticed sometimes it does work and there are no problems but sometimes one or more buyer threads take more orders than they should. with 5 buyer threads there should be 50 outputs but I get anywhere from 50 to 60 lines (orders places).
Turning the amount of threads up to 30 increases the problem and now I can expect an increase of up to 50% more orders with some threads placing up to 30 orders.
Doing some research this is called a data-race and is caused by 2 threads accessing the same data at the same time while 1 of them writes to the data. This basically changes the data such that the other thread isn't working with the same data it expects to be working with.
My attempt:
I firmly believe ReentrantLocks are designed to handle situations like this as they should stop any thread from entering a section of code if another thread hasn't left it. Both the placeOrder(Order order); and handleOrder(); function make use of this mechanic. I'm therefor assuming I didn't implement this correctly. Here is a version of the project which is compileable and executable from a single file called Test.java. Would anyone be able to take a look at that or the code explained above and tell me what I'm doing wrong?
EDIT
I noticed there was a way a buyer could place more than 10 orders so I changed the code to:
/*
* The run method which is ran once the thread is started.
*/
public void run() {
//Run until the thread has bought 10 packages, this ensures the thread
//will eventually stop execution automatically.
for(packsBought = 0; packsBought < 10; packsBought++)
{
try {
//Sleep for a random amount of time between 1 and 50
//milliseconds.
Thread.sleep(this.rand.nextInt(49) + 1);
//Catch any interruptExceptions.
} catch (InterruptedException ex) {
//There is no problem if this exception is thrown, the thread
//will just make an order earlier than planned. that being said
//there should be no manner in which this exception is thrown.
}
//Create a new order.
Order order = new Order(this.rand.nextInt(3)+ 1,
this,
this.isPrime);
//Set the time at which the order was placed as now.
order.setOrderTime(System.currentTimeMillis());
//place the newly created order in the warehouse.
this.warehouse.placeOrder(order);
}
//Notify the thread has finished execution.
System.out.println("Thread: " + super.getName() + " has finished.");
}
in the buyers run(); function yet I'm still getting some threads which place over 10 orders. I also removed the update of the amount of packs bought in the handleOrder(); function as that is now unnecessary. here is an updated version of Test.java (where all classes are together for easy execution) There seems to be a different problem here.
There are some concurrency issues with the code, but the main bug is not related to them: it's in the block starting in line 512 on placeOrder
//Enumerate all non prime orders in the list.
for (int i = inPrime; i < sizeOrderList - 1; i++) {
//Move the non prime order back 1 position in the list.
buffer[i + 1] = buffer[i];
}
when there is only one normal order in the buffer, then inPrime value is 0, inNormal is 1, buffer[0] is the normal order and the rest of the buffer is null.
The code to move non primer orders, starts in index 0, and then does:
buffer[1] = buffer[0] //normal order in 0 get copied to 1
buffer[2] = buffer[1] //now its in 1, so it gets copied to 2
buffer[3] = buffer[2] //now its in 2 too, so it gets copied to 3
....
so it moves the normal order to buffer[1] but then it copies the contents filling all the buffer with that order.
To solve it you should copy the array in reverse order:
//Enumerate all non prime orders in the list.
for (int i = (sizeOrderList-1); i > inPrime; i--) {
//Move the non prime order back 1 position in the list.
buffer[i] = buffer[i-1];
}
As for the concurrency issues:
If you check a field on a thread, updated by another thread you should declare it as volatile. Thats the case of the run field in DispatcherWorker and ResourceSupplier. See: https://stackoverflow.com/a/8063587/11751648
You start interrupting the dispatcher threads (line 183) while they are still processing packages. So if they are stopped at 573, 574 or 579, they will throw an InterruptedException and not finish the processing (hence in the last code not always all packages are delivered). You could avoid this by checking that the buffer is empty before start interrupting dispatcher threads, calling warehouse.notFullBuffer.acquire(warehouse.sizeOrderList); on 175
When catching InterruptedException you should always call Thread.currentThread().interrupt(); the preserve the interrupted status of the Thread. See: https://stackoverflow.com/a/3976377/11751648
I believe you may be chasing ghosts. I'm not entirely sure why you're seeing more outputs than you're expecting, but the number of orders placed appears to be in order. Allow me to clarify:
I've added a Map<String,Integer> to the Warehouse class to map how many orders each thread places:
private Map<String,Integer> ordersPlaced = new TreeMap<>();
// Code omitted for brevity
public void placeOrder(Order order)
{
try
{
//halt untill there are enough packs to handle an order.
this.notFullBuffer.acquire();
//Lock to signify the start of the critical section.
this.mutexBuffer.lock();
ordersPlaced.merge(Thread.currentThread().getName(), 1, Integer::sum);
// Rest of method
}
I then added a for-loop to the main method to execute the code 100 times, and added the following code to the end of each iteration:
warehouse.ordersPlaced.forEach((thread, orders) -> System.out.printf(" %s - %d%n", thread, orders));
I placed a breakpoint inside the lambda expression, with condition orders != 10. This condition never triggered in the 100+ runs I executed. As far as I can tell, your code is working as intended. I've increased both nWorkers and nBuyers to 100 just to be sure.
I believe you're using ReentrantLock correctly, and I agree that it is probably the best choice for your use case.
referring at your code on pastebin
THE GENERIC PROBLEM:
In the function public void handleOrder() he sleep (line 582) Thread.sleep(5 * nPacks); is inside the lock(): unlock(): block.
With this position of sleep, it has no sense to have many DispatchWorker because n-1 will wait at line 559 this.mutexBuffer.lock() while one is sleeping at line 582.
THE BUG:
The bug is in line 173. You should remove it.
In your main() you join all buyers and this is correct. Then you try to stop the workers. The workers at this time are already running to complete orders that will be completed seconds after. You should only set worker.runThread(false); and then join the thead (possibly in two separate loops). This solution really waits for workers to complete orders. Interrupting the thread that is sleeping at line 582 will raise an InterruptedException and the following lines are skipped, in particular line 596 or 600 that update inPrime and in Normal counters generating unpredictable behaviours.
moving line 582 after line 633 and removing line 173 will solve the problem
HOW TO TEST:
My suggestion is to introduce a counter of all Packs boxes generated by supplier and a counter of all boxes ordered and finally check if generated boxes are equals at ordered plus that left in the whorehouse.
Does Java allows output 1, 0? I've tested it very intensively and I cannot get that output. I get only 1, 1 or 0, 0 or 0, 1.
public class Main {
private int x;
private volatile int g;
// Executed by thread #1
public void actor1(){
x = 1;
g = 1;
}
// Executed by thread #2
public void actor2(){
put_on_screen_without_sync(g);
put_on_screen_without_sync(x);
}
}
Why?
On my eye it is possible to get 1, 0. My reasoning.
g is volatile so it causes that memory order will be ensured. So, it looks like:
actor1:
(1) store(x, 1)
(2) store(g, 1)
(3) memory_barrier // on x86
and, I see the following situation:
reorder store(g, 1) before store(x,1) (memory_barrier is after (2)).
Now, run thread #2. So, g = 1, x = 0. Now, we have expected output.
What is incorrect in my reasoning?
Any actions before a volatile write happen before (HB) any subsequent volatile read of the same variable. In your case, the write to x happens before the write to g (due to program order).
So there are only three possibilities:
actor2 runs first and x and g are 0 - output is 0,0
actor1 runs first and x and g are 1 because of the happens before relationship HB - output is 1,1
the methods run concurrently and only x=1 is executed (not g=1) and the output could be either 0,1 or 0,0 (no volatile write so no guarantee)
No, this isn't possible. According to the JMM, anything that was visible to thread 1 when it writes to a volatile field becomes visible to thread 2 when it reads that field.
There is another example similar to yours provided here:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
//uses x - guaranteed to see 42.
}
}
}
You will never see 1, 0, but properly explaining this is not going to be easy, spec wise. First let's get some obvious things out of the door. The specification says:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
This means that on the writing thread side, hb(x, g) and on the reading side hb(g, x). But this is only so, if you would have to reason about each thread individually, as the chapter about Program order says::
Among all the inter-thread actions performed by each thread t...
So if you imagine running each thread at a time, then happens-before would be correct for each of them, individually. But you don't. Your actors (I am sure you use jcstress there) run concurrently. So relying on "program order" for reasoning is not enough (neither it is correct).
You need to somehow synchronize these two actions now - the reading and the writing. And here is how the specification says it can be done:
A write to a volatile variable synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).
And later says:
If an action x synchronizes-with a following action y, then we also have hb(x, y).
If you put all of these together now:
(hb) (hb) (hb)
write(x) ------> write(g) -------> read(g) -------> read(x)
This is also called to "transitively" close program order and synchronizes-with order. Since there is hb on every step, seeing 1, 0 (a racy read), is impossible according to the spec.
No, and in fact this property of volatile is used in classes like ConcurrentHashMap to implement a lock-free happy path, roughly like this:
volatile int locked = 0;
...
void mutate() {
if (Unsafe.compareAndSwapInt(locked,0,1)) {
/*this isn't exactly how you call this method, but the point stands:
if we read 0, we atomically replace it with 1 and continue on the happy
path */
//we are happy
//so we mutate the structure and then
locked = 0;
} else {
//contended lock, we aren't happy
}
}
Since writes before a volatile write can't be reordered after the volatile write, and reads after volatile read can't be reordered before the volatile read, code like this indeed works as a "lockless locking".
Suppose I have an int array, an element num and 4 threads.
I'm giving each thread 1/4 of the array to search for num.
(The search method is given below)
public static boolean contains(int[] array, int minIdx, int maxIdx, int num) { ...}
At my "top level", I can schedule 4 threads to search 4 quarters of the array, but how do I ensure ALL the threads stop searching as soon as one of them finds the element (assuming there is NO duplicate in the array, hence the element can appear at most once).
P.S: You see, suppose my 4th thread found the element at the first iteration, I want the top-level method to return immediately as opposed to wait for other 3 guys to complete.
You need explicit signaling. You might consider the built-in interruption mechanism, or you may roll your own because it's very simple.
One idea: share an AtomicBoolean among all the threads and let each one periodically check it. When a thread finds the answer, it flips the boolean. The best option to achieve periodic checking is a nested loop:
for (int i = start; i < end && !done.get();) {
for (int batchLimit = Math.min(i + BATCH_SIZE, end); i < batchLimit; i++) {
// your logic
}
}
This is the easiest for the JIT compiler to optimize.
The price of checking the value is very low whenever the value didn't change. It will be in the L3 cache. The case when the value did change is irrelevant because at that point you're done.
Use a flag to signal when you found the answer and share it between threads. AtomicBoolean is a good option.
Add the boolean to your loop end conditions for example
for (int i = minIdxs ; i < maxIdxs && found.get() == false; ++i){...}
Also share a CountDownLatch of size 4 and countDown() when you are returning from each thread.
Have your main thread await() and it'll mean all threads gracefully finish before you move on in your main thread.
You can write a class who will act like a controller. this class will know each thread and every thread knows the controller. (its like an observer pattern)
If one thread finds the answer, the thread can tell it to the controller which can inform the other threads to stop.
class ControllerOfAllTheThreads{
ArrayList<TheClassesWhichDoTheSearch> list = new ArrayList<TheClassesWhichDoTheSearch>();
public void tellThemWeFoundHim(){
for (TheClassesWhichDoTheSearch theThreads : list) {
if(theThreads.isAlive() && !theThreads.isInterrupted())
theThreads.interrupt();
}
}
}
This simple program has a shared array and 2 threads:
first thread - shows sum of values in the array.
second thread - subtracts 200 from one cell of the array and adds 200 to another cell.
I would expect to see the results: 1500 (sum of the array), 1300 (if the display occurs between the subtraction and the addition).
But for some reason, sometimes 1100 and 1700 appear, which I can't explain...
public class MainClass {
public static void main(String[] args) {
Bank bank = new Bank();
bank.CurrentSum.start();
bank.TransferMoney.start();
}
}
class Bank {
private int[] Accounts = { 100, 200, 300, 400, 500 };
private Random rnd = new Random();
Thread CurrentSum = new Thread("Show sum") {
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println(Accounts[0] + Accounts[1] + Accounts[2]
+ Accounts[3] + Accounts[4]);
}
}
};
Thread TransferMoney = new Thread("Tranfer"){
public void run(){
for(int i=0; i<50000; i++)
{
Accounts[rnd.nextInt(5)]-=200;
Accounts[rnd.nextInt(5)]+=200;
}
}
};
}
You are not updating the values in an atomic or thread safe manner. This means sometimes you see two more -200 than +200 and sometimes you see two more +200 than -200. As you iterate over the values it is possible to see a +200 value but the -200 value is an earlier value and you miss it, but you see another +200 update again missing the -200 change.
It should be possible to see up to 5 x +200 or 5 x -200 in rare cases.
It's happening because the addition of the five values is not atomic, and may be interrupted by the decrement and increment happening in the other thread.
Here's a possible case.
The display thread adds Accounts[0]+Accounts[1]+Accounts[2].
The updating thread decrements Accounts[0] and increments Accounts[3].
The updating thread decrements Accounts[1] and increments Accounts[4].
The display thread continues with its addition, adding Accounts[3] and Accounts[4] to the sum that it had already partially evaluated.
In this case, the sum will be 1900, because you've included two values after they've been incremented.
You should be able to work out cases like this, to give you sums of anything between 700 and 2300.
Perhaps on purpose, you are not doing the addition operation atomically.
That means that this line:
System.out.println(Accounts[0] + Accounts[1] + Accounts[2]
+ Accounts[3] + Accounts[4]);
Will run in multiple steps, any of which can occur during any iteration of the second thread.
1. Get value of Accounts[0] = a
2. Get value of Accounts[1] = b
...So on
The addition then happens after all the values are pulled from the array.
You can imagine that 200 is subtracted from Accounts[0], which is dereferenced by the JRE, then in another loop of the second thread, 200 is removed from Accounts[1], which is subsequently dereferenced by the JRE. This can result in the the output you see.
The Accounts variable is being accessed from more than one thread, one of which modifies its value. In order for the other thread to reliably read the modified values at all it is necessary to use a "memory barrier". Java has a number of ways of providing a memory barrier: synchronized, volatile or one of the Atomic types are the most common.
The Bank class also has some logic which requires the modifications to be made in multiple steps before the Accounts variable is back in a consistent state. The synchronized keyword can also be used to prevent another block of code that is synchronised on the same object from running until the first synchronized block has completed.
This implementation of the Bank class locks all access to the Accounts variable using the mutex lock object of the Bank object that owns the Accounts variable. This ensures that each synchronised block is run in its entirety before the other thread can run its own synchronised block. It also ensures that changes to the Accounts variable are visible to the other thread:
class Bank {
private int[] Accounts = { 100, 200, 300, 400, 500 };
private Random rnd = new Random();
Thread CurrentSum = new Thread("Show sum") {
public void run() {
for (int i = 0; i < 500; i++) {
printAccountsTotal();
}
}
};
Thread TransferMoney = new Thread("Tranfer"){
public void run(){
for(int i=0; i<50000; i++)
{
updateAccounts();
}
}
};
synchronized void printAccountsTotal() {
System.out.println(Accounts[0] + Accounts[1] + Accounts[2]
+ Accounts[3] + Accounts[4]);
}
synchronized void updateAccounts() {
Accounts[rnd.nextInt(5)]-=200;
Accounts[rnd.nextInt(5)]+=200;
}
}
Why is i++ not atomic in Java?
To get a bit deeper in Java I tried to count how often the loop in threads are executed.
So I used a
private static int total = 0;
in the main class.
I have two threads.
Thread 1: Prints System.out.println("Hello from Thread 1!");
Thread 2: Prints System.out.println("Hello from Thread 2!");
And I count the lines printed by thread 1 and thread 2. But the lines of thread 1 + lines of thread 2 don't match the total number of lines printed out.
Here is my code:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test {
private static int total = 0;
private static int countT1 = 0;
private static int countT2 = 0;
private boolean run = true;
public Test() {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
newCachedThreadPool.execute(t1);
newCachedThreadPool.execute(t2);
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
run = false;
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println((countT1 + countT2 + " == " + total));
}
private Runnable t1 = new Runnable() {
#Override
public void run() {
while (run) {
total++;
countT1++;
System.out.println("Hello #" + countT1 + " from Thread 2! Total hello: " + total);
}
}
};
private Runnable t2 = new Runnable() {
#Override
public void run() {
while (run) {
total++;
countT2++;
System.out.println("Hello #" + countT2 + " from Thread 2! Total hello: " + total);
}
}
};
public static void main(String[] args) {
new Test();
}
}
i++ is probably not atomic in Java because atomicity is a special requirement which is not present in the majority of the uses of i++. That requirement has a significant overhead: there is a large cost in making an increment operation atomic; it involves synchronization at both the software and hardware levels that need not be present in an ordinary increment.
You could make the argument that i++ should have been designed and documented as specifically performing an atomic increment, so that a non-atomic increment is performed using i = i + 1. However, this would break the "cultural compatibility" between Java, and C and C++. As well, it would take away a convenient notation which programmers familiar with C-like languages take for granted, giving it a special meaning that applies only in limited circumstances.
Basic C or C++ code like for (i = 0; i < LIMIT; i++) would translate into Java as for (i = 0; i < LIMIT; i = i + 1); because it would be inappropriate to use the atomic i++. What's worse, programmers coming from C or other C-like languages to Java would use i++ anyway, resulting in unnecessary use of atomic instructions.
Even at the machine instruction set level, an increment type operation is usually not atomic for performance reasons. In x86, a special instruction "lock prefix" must be used to make the inc instruction atomic: for the same reasons as above. If inc were always atomic, it would never be used when a non-atomic inc is required; programmers and compilers would generate code that loads, adds 1 and stores, because it would be way faster.
In some instruction set architectures, there is no atomic inc or perhaps no inc at all; to do an atomic inc on MIPS, you have to write a software loop which uses the ll and sc: load-linked, and store-conditional. Load-linked reads the word, and store-conditional stores the new value if the word has not changed, or else it fails (which is detected and causes a re-try).
i++ involves two operations :
read the current value of i
increment the value and assign it to i
When two threads perform i++ on the same variable at the same time, they may both get the same current value of i, and then increment and set it to i+1, so you'll get a single incrementation instead of two.
Example :
int i = 5;
Thread 1 : i++;
// reads value 5
Thread 2 : i++;
// reads value 5
Thread 1 : // increments i to 6
Thread 2 : // increments i to 6
// i == 6 instead of 7
Java specification
The important thing is the JLS (Java Language Specification) rather than how various implementations of the JVM may or may not have implemented a certain feature of the language.
The JLS defines the ++ postfix operator in clause 15.14.2 which says i.a. "the value 1 is added to the value of the variable and the sum is stored back into the variable". Nowhere does it mention or hint at multithreading or atomicity.
For multithreading or atomicity, the JLS provides volatile and synchronized. Additionally, there are the Atomic… classes.
Why is i++ not atomic in Java?
Let's break the increment operation into multiple statements:
Thread 1 & 2 :
Fetch value of total from memory
Add 1 to the value
Write back to the memory
If there is no synchronization then let's say Thread one has read the value 3 and incremented it to 4, but has not written it back. At this point, the context switch happens. Thread two reads the value 3, increments it and the context switch happens. Though both threads have incremented the total value, it will still be 4 - race condition.
i++ is a statement which simply involves 3 operations:
Read current value
Write new value
Store new value
These three operations are not meant to be executed in a single step or in other words i++ is not a compound operation. As a result all sorts of things can go wrong when more than one threads are involved in a single but non-compound operation.
Consider the following scenario:
Time 1:
Thread A fetches i
Thread B fetches i
Time 2:
Thread A overwrites i with a new value say -foo-
Thread B overwrites i with a new value say -bar-
Thread B stores -bar- in i
// At this time thread B seems to be more 'active'. Not only does it overwrite
// its local copy of i but also makes it in time to store -bar- back to
// 'main' memory (i)
Time 3:
Thread A attempts to store -foo- in memory effectively overwriting the -bar-
value (in i) which was just stored by thread B in Time 2.
Thread B has nothing to do here. Its work was done by Time 2. However it was
all for nothing as -bar- was eventually overwritten by another thread.
And there you have it. A race condition.
That's why i++ is not atomic. If it was, none of this would have happened and each fetch-update-store would happen atomically. That's exactly what AtomicInteger is for and in your case it would probably fit right in.
P.S.
An excellent book covering all of those issues and then some is this:
Java Concurrency in Practice
In the JVM, an increment involves a read and a write, so it's not atomic.
If the operation i++ would be atomic you wouldn't have the chance to read the value from it. This is exactly what you want to do using i++ (instead of using ++i).
For example look at the following code:
public static void main(final String[] args) {
int i = 0;
System.out.println(i++);
}
In this case we expect the output to be: 0
(because we post increment, e.g. first read, then update)
This is one of the reasons the operation can't be atomic, because you need to read the value (and do something with it) and then update the value.
The other important reason is that doing something atomically usually takes more time because of locking. It would be silly to have all the operations on primitives take a little bit longer for the rare cases when people want to have atomic operations. That is why they've added AtomicInteger and other atomic classes to the language.
There are two steps:
fetch i from memory
set i+1 to i
so it's not atomic operation.
When thread1 executes i++, and thread2 executes i++, the final value of i may be i+1.
In JVM or any VM, the i++ is equivalent to the following:
int temp = i; // 1. read
i = temp + 1; // 2. increment the value then 3. write it back
that is why i++ is non-atomic.
Concurrency (the Thread class and such) is an added feature in v1.0 of Java. i++ was added in the beta before that, and as such is it still more than likely in its (more or less) original implementation.
It is up to the programmer to synchronize variables. Check out Oracle's tutorial on this.
Edit: To clarify, i++ is a well defined procedure that predates Java, and as such the designers of Java decided to keep the original functionality of that procedure.
The ++ operator was defined in B (1969) which predates java and threading by just a tad.