Java volatile variable doesn't behave correctly.
public class MyThread
{
volatile static int i;
public static class myT extends Thread
{
public void run ()
{
int j = 0;
while(j<1000000){
i++;
j++;
}
}
}
public static void main (String argv)
throws InterruptedException{
i = 0;
Thread my1 = new myT();
Thread my2 = new myT();
my1.start();
my2.start();
my1.join();
my2.join();
System.out.println("i = "+i);
}
}
Since volatile builds happens-before relationship, the final value of i should be strictly 2000000. However, the actual result is nothing different from being without volatile for variable i. Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
java concurrency volatile
add a comment |
public class MyThread
{
volatile static int i;
public static class myT extends Thread
{
public void run ()
{
int j = 0;
while(j<1000000){
i++;
j++;
}
}
}
public static void main (String argv)
throws InterruptedException{
i = 0;
Thread my1 = new myT();
Thread my2 = new myT();
my1.start();
my2.start();
my1.join();
my2.join();
System.out.println("i = "+i);
}
}
Since volatile builds happens-before relationship, the final value of i should be strictly 2000000. However, the actual result is nothing different from being without volatile for variable i. Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
java concurrency volatile
add a comment |
public class MyThread
{
volatile static int i;
public static class myT extends Thread
{
public void run ()
{
int j = 0;
while(j<1000000){
i++;
j++;
}
}
}
public static void main (String argv)
throws InterruptedException{
i = 0;
Thread my1 = new myT();
Thread my2 = new myT();
my1.start();
my2.start();
my1.join();
my2.join();
System.out.println("i = "+i);
}
}
Since volatile builds happens-before relationship, the final value of i should be strictly 2000000. However, the actual result is nothing different from being without volatile for variable i. Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
java concurrency volatile
public class MyThread
{
volatile static int i;
public static class myT extends Thread
{
public void run ()
{
int j = 0;
while(j<1000000){
i++;
j++;
}
}
}
public static void main (String argv)
throws InterruptedException{
i = 0;
Thread my1 = new myT();
Thread my2 = new myT();
my1.start();
my2.start();
my1.join();
my2.join();
System.out.println("i = "+i);
}
}
Since volatile builds happens-before relationship, the final value of i should be strictly 2000000. However, the actual result is nothing different from being without volatile for variable i. Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
java concurrency volatile
java concurrency volatile
asked Apr 29 '13 at 23:39
OneZeroOneZero
4,23084167
4,23084167
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
It is protected but unfortunately i++
is not an atomic operation. It is actually read/increment/store. So volatile
is not going to save you from the race conditions between threads. You might get the following order of operations from your program:
- thread #1 reads
i
, gets 10 - right afterwards, thread #2 reads
i
, gets 10 - thread #1 increments
i
to 11 - thread #2 increments
i
to 11 - thread #1 stores 11 to
i
- thread #2 stores 11 to
i
As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?
What you should be using are AtomicInteger
which allows you to safely increment from multiple threads.
static final AtomicInteger i = new AtomicInteger(0);
...
for (int j = 0; j<1000000; j++) {
i.incrementAndGet();
}
1
Onlyi
needs to be anAtomicInteger
;j
is purely local to the thread.
– Mel Nicholson
Apr 29 '13 at 23:44
1
I'm not sure how to answer @OneZero. Declaring it asvolatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or useAtomicInteger
. Making itvolatile
isn't enough.
– Gray
Apr 29 '13 at 23:46
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about whatvolitile
is for. Not this.
– Mel Nicholson
Apr 29 '13 at 23:47
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than aword
on the underlying memory.double
is the most common offender. Generally speaking, most concurrency issues are too much forvolatile
so look atsynchronize
andwait
instead.
– Mel Nicholson
Apr 29 '13 at 23:55
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have adouble data
and avolitile boolean ready
and code that setsdata
first andready = true
second, it is certain that after you seeready
turn to true,data
will also have been set, even in a different thread. This is not guaranteed without thevolatile
keyword, where you can seeready
true and then the value ofdata
may not have propagated from the write thread.
– Mel Nicholson
Apr 30 '13 at 0:03
|
show 10 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f16289983%2fjava-volatile-variable-doesnt-behave-correctly%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
It is protected but unfortunately i++
is not an atomic operation. It is actually read/increment/store. So volatile
is not going to save you from the race conditions between threads. You might get the following order of operations from your program:
- thread #1 reads
i
, gets 10 - right afterwards, thread #2 reads
i
, gets 10 - thread #1 increments
i
to 11 - thread #2 increments
i
to 11 - thread #1 stores 11 to
i
- thread #2 stores 11 to
i
As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?
What you should be using are AtomicInteger
which allows you to safely increment from multiple threads.
static final AtomicInteger i = new AtomicInteger(0);
...
for (int j = 0; j<1000000; j++) {
i.incrementAndGet();
}
1
Onlyi
needs to be anAtomicInteger
;j
is purely local to the thread.
– Mel Nicholson
Apr 29 '13 at 23:44
1
I'm not sure how to answer @OneZero. Declaring it asvolatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or useAtomicInteger
. Making itvolatile
isn't enough.
– Gray
Apr 29 '13 at 23:46
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about whatvolitile
is for. Not this.
– Mel Nicholson
Apr 29 '13 at 23:47
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than aword
on the underlying memory.double
is the most common offender. Generally speaking, most concurrency issues are too much forvolatile
so look atsynchronize
andwait
instead.
– Mel Nicholson
Apr 29 '13 at 23:55
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have adouble data
and avolitile boolean ready
and code that setsdata
first andready = true
second, it is certain that after you seeready
turn to true,data
will also have been set, even in a different thread. This is not guaranteed without thevolatile
keyword, where you can seeready
true and then the value ofdata
may not have propagated from the write thread.
– Mel Nicholson
Apr 30 '13 at 0:03
|
show 10 more comments
Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
It is protected but unfortunately i++
is not an atomic operation. It is actually read/increment/store. So volatile
is not going to save you from the race conditions between threads. You might get the following order of operations from your program:
- thread #1 reads
i
, gets 10 - right afterwards, thread #2 reads
i
, gets 10 - thread #1 increments
i
to 11 - thread #2 increments
i
to 11 - thread #1 stores 11 to
i
- thread #2 stores 11 to
i
As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?
What you should be using are AtomicInteger
which allows you to safely increment from multiple threads.
static final AtomicInteger i = new AtomicInteger(0);
...
for (int j = 0; j<1000000; j++) {
i.incrementAndGet();
}
1
Onlyi
needs to be anAtomicInteger
;j
is purely local to the thread.
– Mel Nicholson
Apr 29 '13 at 23:44
1
I'm not sure how to answer @OneZero. Declaring it asvolatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or useAtomicInteger
. Making itvolatile
isn't enough.
– Gray
Apr 29 '13 at 23:46
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about whatvolitile
is for. Not this.
– Mel Nicholson
Apr 29 '13 at 23:47
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than aword
on the underlying memory.double
is the most common offender. Generally speaking, most concurrency issues are too much forvolatile
so look atsynchronize
andwait
instead.
– Mel Nicholson
Apr 29 '13 at 23:55
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have adouble data
and avolitile boolean ready
and code that setsdata
first andready = true
second, it is certain that after you seeready
turn to true,data
will also have been set, even in a different thread. This is not guaranteed without thevolatile
keyword, where you can seeready
true and then the value ofdata
may not have propagated from the write thread.
– Mel Nicholson
Apr 30 '13 at 0:03
|
show 10 more comments
Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
It is protected but unfortunately i++
is not an atomic operation. It is actually read/increment/store. So volatile
is not going to save you from the race conditions between threads. You might get the following order of operations from your program:
- thread #1 reads
i
, gets 10 - right afterwards, thread #2 reads
i
, gets 10 - thread #1 increments
i
to 11 - thread #2 increments
i
to 11 - thread #1 stores 11 to
i
- thread #2 stores 11 to
i
As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?
What you should be using are AtomicInteger
which allows you to safely increment from multiple threads.
static final AtomicInteger i = new AtomicInteger(0);
...
for (int j = 0; j<1000000; j++) {
i.incrementAndGet();
}
Can anyone explanation why it doesn't work here? Since i is declared volatile, it should be protected from memory inconsistency.
It is protected but unfortunately i++
is not an atomic operation. It is actually read/increment/store. So volatile
is not going to save you from the race conditions between threads. You might get the following order of operations from your program:
- thread #1 reads
i
, gets 10 - right afterwards, thread #2 reads
i
, gets 10 - thread #1 increments
i
to 11 - thread #2 increments
i
to 11 - thread #1 stores 11 to
i
- thread #2 stores 11 to
i
As you can see, even though 2 increments have happened and the value has been properly synchronized between threads, the race condition means the value only went up by 1. See this nice looking explanation. Here's another good answer: Is a volatile int in Java thread-safe?
What you should be using are AtomicInteger
which allows you to safely increment from multiple threads.
static final AtomicInteger i = new AtomicInteger(0);
...
for (int j = 0; j<1000000; j++) {
i.incrementAndGet();
}
edited May 23 '17 at 10:25
Community♦
11
11
answered Apr 29 '13 at 23:41
GrayGray
95.9k14229298
95.9k14229298
1
Onlyi
needs to be anAtomicInteger
;j
is purely local to the thread.
– Mel Nicholson
Apr 29 '13 at 23:44
1
I'm not sure how to answer @OneZero. Declaring it asvolatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or useAtomicInteger
. Making itvolatile
isn't enough.
– Gray
Apr 29 '13 at 23:46
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about whatvolitile
is for. Not this.
– Mel Nicholson
Apr 29 '13 at 23:47
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than aword
on the underlying memory.double
is the most common offender. Generally speaking, most concurrency issues are too much forvolatile
so look atsynchronize
andwait
instead.
– Mel Nicholson
Apr 29 '13 at 23:55
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have adouble data
and avolitile boolean ready
and code that setsdata
first andready = true
second, it is certain that after you seeready
turn to true,data
will also have been set, even in a different thread. This is not guaranteed without thevolatile
keyword, where you can seeready
true and then the value ofdata
may not have propagated from the write thread.
– Mel Nicholson
Apr 30 '13 at 0:03
|
show 10 more comments
1
Onlyi
needs to be anAtomicInteger
;j
is purely local to the thread.
– Mel Nicholson
Apr 29 '13 at 23:44
1
I'm not sure how to answer @OneZero. Declaring it asvolatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or useAtomicInteger
. Making itvolatile
isn't enough.
– Gray
Apr 29 '13 at 23:46
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about whatvolitile
is for. Not this.
– Mel Nicholson
Apr 29 '13 at 23:47
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than aword
on the underlying memory.double
is the most common offender. Generally speaking, most concurrency issues are too much forvolatile
so look atsynchronize
andwait
instead.
– Mel Nicholson
Apr 29 '13 at 23:55
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have adouble data
and avolitile boolean ready
and code that setsdata
first andready = true
second, it is certain that after you seeready
turn to true,data
will also have been set, even in a different thread. This is not guaranteed without thevolatile
keyword, where you can seeready
true and then the value ofdata
may not have propagated from the write thread.
– Mel Nicholson
Apr 30 '13 at 0:03
1
1
Only
i
needs to be an AtomicInteger
; j
is purely local to the thread.– Mel Nicholson
Apr 29 '13 at 23:44
Only
i
needs to be an AtomicInteger
; j
is purely local to the thread.– Mel Nicholson
Apr 29 '13 at 23:44
1
1
I'm not sure how to answer @OneZero. Declaring it as
volatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or use AtomicInteger
. Making it volatile
isn't enough.– Gray
Apr 29 '13 at 23:46
I'm not sure how to answer @OneZero. Declaring it as
volatile
won't work because ++ is not atomic. You could synchronize on it every time you update it or use AtomicInteger
. Making it volatile
isn't enough.– Gray
Apr 29 '13 at 23:46
2
2
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about what
volitile
is for. Not this.– Mel Nicholson
Apr 29 '13 at 23:47
@OneZero docs.oracle.com/javase/tutorial/essential/concurrency/… should explain a bit about what
volitile
is for. Not this.– Mel Nicholson
Apr 29 '13 at 23:47
1
1
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than a
word
on the underlying memory. double
is the most common offender. Generally speaking, most concurrency issues are too much for volatile
so look at synchronize
and wait
instead.– Mel Nicholson
Apr 29 '13 at 23:55
@OneZero a non-volitile read can get some bits for the pre-write value, and others from a post-write value, but only for types larger than a
word
on the underlying memory. double
is the most common offender. Generally speaking, most concurrency issues are too much for volatile
so look at synchronize
and wait
instead.– Mel Nicholson
Apr 29 '13 at 23:55
1
1
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have a
double data
and a volitile boolean ready
and code that sets data
first and ready = true
second, it is certain that after you see ready
turn to true, data
will also have been set, even in a different thread. This is not guaranteed without the volatile
keyword, where you can see ready
true and then the value of data
may not have propagated from the write thread.– Mel Nicholson
Apr 30 '13 at 0:03
@Gray In practice on anything build this millenium, probably true. I don't think the guarantee is in the language spec, though. Perhaps a more practical guarantee is that write-order is preserved, so if you have a
double data
and a volitile boolean ready
and code that sets data
first and ready = true
second, it is certain that after you see ready
turn to true, data
will also have been set, even in a different thread. This is not guaranteed without the volatile
keyword, where you can see ready
true and then the value of data
may not have propagated from the write thread.– Mel Nicholson
Apr 30 '13 at 0:03
|
show 10 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f16289983%2fjava-volatile-variable-doesnt-behave-correctly%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown