public class VolatileTest {
private volatile static int a = 0;
private static int cnt = 0;
public static void test() {
if (a == 0) {
a = 1;
cnt++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[2];
for (int i = 0; i < 10000; i++) {
for (int j = 0; j < 2; j++) {
threads[j] = new Thread(VolatileTest::test);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
if (cnt == 2) {
System.out.println("false!");
}
a = 0;
cnt = 0;
}
System.out.println("finish");
}
}
在反复启动程序的尝试中,一般出现的情况是控制台出现一次 false!,偶尔会出现两次或者不出现。
希望能达成 false!永远不会被输出,即 cnt 结束时总为 1 的结果,能否在不引入 Atomic*类,synchrnozied,加锁等更高级工具,只使用 volatile 实现?
cnt ++ 代表着希望被执行一次的代码。可能会有多个线程调用 VolatileTest.test(),但 cnt++只执行一次。
谢谢
1
geelaw 2021-04-24 23:38:36 +08:00 via iPhone
不能。
|
2
Newyorkcity OP @geelaw 那 volatile 的保持可见性,以及一些文章提到 volatile boolean 可能靠谱的情况是什么呢。。
|
3
Jooooooooo 2021-04-24 23:49:14 +08:00 1
当然不行, 两个线程会同时运行到 if(a==0) 并且发现 a 确实都是 0
|
4
Newyorkcity OP @geelaw 是因为 voliatle 永远无法保证 『线程 b 执行完 if a == 0 』这件事不发生在 『线程 a 执行完 if a == 0 』和『线程 a 执行 a = 1 』之间吗?
|
5
Leviathann 2021-04-25 01:12:38 +08:00 via iPhone
借楼问一下
我看项目里以前的代码有用 volatile 修饰一个 service 里的 map,而 map 除了初始化有个赋值为 new hashmap,其他都只有 get set 这种 volatile 有什么用? volatile 应该只涉及到 map 的这个引用不涉及到内部元素的吧 还是我理解错了 |
6
cubecube 2021-04-25 01:57:34 +08:00
@Leviathann 你理解得没错,除非会并发访问并存在更新 map 本身为另外一个 hashmap or null,正常 map 没必要加
|
7
EscYezi 2021-04-25 09:08:32 +08:00 via iPhone
@Newyorkcity #4 是的,因为判断 a 是否为 0 和给 a 赋值 1 是两个操作,想要达到预期的效果必须把这个两个操作变成一个原子操作
|
8
bxb100 2021-04-25 09:57:26 +08:00
操作和可见不是一件事情吧
|
9
eric96 2021-04-25 09:57:32 +08:00
两个线程同时判断到 a==0,然后进入条件语句,将 a 赋值为 1.volatile 只是保证了可见性和不重排,但是你对 a 的判断和赋值是两个操作,不是原子的
|
10
securityCoding 2021-04-25 10:02:42 +08:00 via Android
@Leviathann 这是在瞎用了,volatile 一般结合 cas 实现无锁并发读写。
|
11
eric96 2021-04-25 10:07:03 +08:00
要么加锁,要么用 Atomic 。其实可以看下 Atomic 的实现,也就是 volatile 加上 cas 操作
|
13
theOneMe 2021-04-25 11:41:35 +08:00
多于两步的操作,要么无锁同步加可见行,要么加锁。
|
14
inhzus 2021-04-25 13:04:01 +08:00 via iPhone
有 自己用 volatile 实现一遍 cas,也挺简单的 /doge
|
15
wqhui 2021-04-25 13:52:50 +08:00
没记错的话 volatile 只是每次去内存读值,不使用缓存,保证每次读到的值是最新的。但是运气不好的话,一个线程读到了 a=0,在做 a=1 的赋值操作前这段时间,其他线程也是能够进来的
|
16
wolfie 2021-04-25 13:53:42 +08:00
要是能实现,Atomic* 情何以堪。
|
17
LukeChien 2021-04-25 18:26:31 +08:00
如果你希望原子执行的代码都如 cnt++ 这么简单,那你可以换个单核的 CPU :)
|