[ACCEPTED]-Are java primitive ints atomic by design or by accident?-multithreading
All memory accesses in Java are atomic by 38 default, with the exception of long
and double
(which 37 may be atomic, but don't have to be). It's 36 not put very clearly to be honest, but I believe 35 that's the implication.
From section 17.4.3 of the JLS:
Within 34 a sequentially consistent execution, there 33 is a total order over all individual actions 32 (such as reads and writes) which is consistent 31 with the order of the program, and each individual 30 action is atomic and is immediately visible 29 to every thread.
and then in 17.7:
Some implementations 28 may find it convenient to divide a single 27 write action on a 64-bit long or double value 26 into two write actions on adjacent 32 25 bit values. For efficiency's sake, this 24 behavior is implementation specific; Java 23 virtual machines are free to perform writes 22 to long and double values atomically or in 21 two parts.
Note that atomicity is very different 20 to volatility though.
When one thread updates 19 an integer to 5, it's guaranteed that another 18 thread won't see 1 or 4 or any other in-between 17 state, but without any explicit volatility 16 or locking, the other thread could see 0 15 forever.
With regard to working hard to get 14 atomic access to bytes, you're right: the 13 VM may well have to try hard... but it does 12 have to. From section 17.6 of the spec:
Some processors 11 do not provide the ability to write to 10 a single byte. It would be illegal to 9 implement byte array updates on such a 8 processor by simply reading an entire 7 word, updating the appropriate byte, and then 6 writing the entire word back to memory. This 5 problem is sometimes known as word tearing, and 4 on processors that cannot easily update 3 a single byte in isolation some other approach 2 will be required.
In other words, it's up 1 to the JVM to get it right.
- No amount of testing can prove thread safety - it can only disprove it;
- I found a indirect reference in JLS 17.7 which states
Some implementations may find it convenient 8 to divide a single write action on a 64-bit 7 long or double value into two write actions 6 on adjacent 32 bit values.
and further down
For 5 the purposes of the Java programming language 4 memory model, a single write to a non-volatile 3 long or double value is treated as two separate 2 writes: one to each 32-bit half.
This seems 1 to imply that writes to ints are atomic.
A read or write from integer or any smaller 17 type should be atomic, but as Robert noted, longs 16 and doubles may or may not depending on 15 the implementation. However, any operation 14 that uses both a read and a write, including 13 all of the increment operators, are not 12 atomic. Thus, if you have to threads operating 11 on an integer i=0, one does i++ and the 10 other does i=10, the result could be 1, 10, or 9 11.
For operations like this, you should 8 look at AtomicInteger which has methods for atomically 7 modifying a value while retrieving the old 6 one or to atomically increment the value.
Finally, threads 5 may cache the value of the variable and 4 won't see changes made to it from other 3 threads. To make sure that both threads 2 always see changes made by the other thread, you 1 need to mark the variable as being volatile.
I agree with Jon Skeet and I would like 19 to add that many people confuse the concept 18 of atomicity, volatility and thread safety 17 because sometimes the terms are used interchangeably.
For 16 example, consider this:
private static int i = 0;
public void increment() { i++; }
While someone may 15 argue that this operation is atomic, the 14 referred hypothesis is wrong.
The statement 13 i++;
performs three operations:
1) Read
2) Update
3) Write
Therefore, the 12 threads that operate on this variable should 11 be synchronized like this:
private static int i = 0;
private static final Object LOCK = new Object();
public void increment() {
synchronized(LOCK) {
i++;
}
}
or this:
private static int i = 0;
public static synchronized void increment() {
i++;
}
Do note 10 that, for a single object instance, calling 9 a method that is being accessed by multiple 8 threads and operate on shared mutable data 7 one has to take into consideration the fact 6 that a method's parameters, local variable 5 and return value are local for each Thread.
For 4 more information check out this link:
http://www.javamex.com/tutorials/synchronization_volatile.shtml
Hope 3 this helps.
UPDATE: There is also the case where 2 you can synchronize on the class object 1 itself. More info here: How to synchronize a static variable among threads running different instances of a class in java?
I think it doesnt work as you expected:
private static int i = 0;
public void increment() {
synchronized (i) {
i++;
}
}
integer 7 is immutable so you are sychronizing on 6 a different object all the time. int "i" is 5 autoboxed to Integer object, then you set 4 lock on it. If another thread goes into 3 this method int i is autoboxed to another 2 Integer object and you set lock on a different 1 object then before.
This is not atomic:
i++;
However, this is:
i = 5;
I think 1 this is where some confusion sets in.
This is somewhat complicated, and is related 2 to system wordsize. Bruce Eckel discusses 1 it in more detail: Java Threads.
Atomic reads and writes merely mean that 16 you will never read, e.g. the first 16 bits 15 of an int update and another from an old 14 value.
This says nothing about WHEN other 13 threads see these writes.
The long story 12 short is that when two threads race with 11 no memory barriers between them something 10 gets lost.
Spin up two or more threads that 9 increment a single shared integer and also 8 count their own increments. When the integer 7 gets to some value (INT_MAX, for example. Nice 6 and large to let things warm up) stop everything 5 and return the value of the int and the 4 number of increments each thread performed.
import java.util.Stack;
public class Test{
static int ctr = Integer.MIN_VALUE;
final static int THREADS = 4;
private static void runone(){
ctr = 0;
Stack<Thread> threads = new Stack<>();
for(int i = 0; i < THREADS; i++){
Thread t = new Thread(new Runnable(){
long cycles = 0;
@Override
public void run(){
while(ctr != Integer.MAX_VALUE){
ctr++;
cycles++;
}
System.out.println("Cycles: " + cycles + ", ctr: " + ctr);
}
});
t.start();
threads.push(t);
}
while(!threads.isEmpty())
try{
threads.pop().join();
}catch(InterruptedException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println();
}
public static void main(String args[]){
System.out.println("Int Range: " + ((long) Integer.MAX_VALUE - (long) Integer.MIN_VALUE));
System.out.println(" Int Max: " + Integer.MAX_VALUE);
System.out.println();
for(;;)
runone();
}
}
Here 3 is the result of this test on my quad core 2 box (feel free to play with the thread count 1 in the code, I just matched my core count, obviously):
Int Range: 4294967295
Int Max: 2147483647
Cycles: 2145700893, ctr: 76261202
Cycles: 2147479716, ctr: 1825148133
Cycles: 2146138184, ctr: 1078605849
Cycles: 2147282173, ctr: 2147483647
Cycles: 2147421893, ctr: 127333260
Cycles: 2146759053, ctr: 220350845
Cycles: 2146742845, ctr: 450438551
Cycles: 2146537691, ctr: 2147483647
Cycles: 2110149932, ctr: 696604594
Cycles: 2146769437, ctr: 2147483647
Cycles: 2147095646, ctr: 2147483647
Cycles: 2147483647, ctr: 2147483647
Cycles: 2147483647, ctr: 330141890
Cycles: 2145029662, ctr: 2147483647
Cycles: 2143136845, ctr: 2147483647
Cycles: 2147007903, ctr: 2147483647
Cycles: 2147483647, ctr: 197621458
Cycles: 2076982910, ctr: 2147483647
Cycles: 2125642094, ctr: 2147483647
Cycles: 2125321197, ctr: 2147483647
Cycles: 2132759837, ctr: 330963474
Cycles: 2102475117, ctr: 2147483647
Cycles: 2147390638, ctr: 2147483647
Cycles: 2147483647, ctr: 2147483647
When data is being shared between threads, synchronization 17 is needed. When dealing with an Integer, which 16 can go from main Memory to a processor's 15 cache in a multiple processor system, a 14 thread may be updating a local copy of an 13 integer tied to a specific processor.
The 12 volatile (See Wiki in Java Section) keyword in Java will ensure any 11 update to an Integer will happen in memory 10 and not a local copy.
Further, to synchronize 9 updates to an Integer, consider using AtomicInteger. This 8 implementation has a method (compareAndSet) to check if 7 a value is what a thread expects AND set 6 it if does. If it doesn't match, then another 5 thread could have updated the value. The 4 AtomicInteger will perform both the reading 3 and updating of the Integer in an atomic 2 operation, with the advantage of not having 1 to block.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.