昨天闲来无事做,在 GitHub 上看到了并发的的例子,其中有个一直不知道怎么解决。感觉都快魔障了
还望各位大佬不吝赐教
1
Banxiaozhuan 2019-09-27 17:23:12 +08:00
volatile int state1;
volatile int state2; 这两个变量并不是同步更新的, 他们之间的更新可以发生并发问题。 |
2
JsonTu OP @Banxiaozhuan 菜鸟一枚,只会加 volatile、synchronized。我写个 synchronized setState(int state1, int state2),也是不行,把想到的,都试过一边,也不行
|
3
Yuicon 2019-09-27 17:34:06 +08:00
private static class CombinationStatTask implements Runnable {
// 对于组合状态,加 volatile 不能解决问题 volatile int state1; volatile int state2; public void next(int i1, int i2) { synchronized (this) { state1 = i1; state2 = i2; } } @Override public void run() { int c = 0; for (long i = 0; ; i++) { int i1, i2; synchronized (this) { i1 = state1; i2 = state2; } if (i1 * 2 != i2) { c++; System.err.printf("Fuck! Got invalid CombinationStat!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n", i + 1, c, (float) c / (i + 1) * 100, i1, i2); } else { // 如果去掉这个输出,则在我的开发机上,发生无效组合的概率由 ~5% 降到 ~0.1% System.out.printf("Emm... %s|%s\n", i1, i2); } } } } |
4
Yuicon 2019-09-27 17:34:33 +08:00
虽然排版没了 但是这样就行了 性能后面优化
|
5
LeeSeoung 2019-09-27 17:36:34 +08:00
task.state1 = rand;
task.state2 = rand * 2; 可能执行完第一行代码 然后就去执行你 task 里的判断了,这两个操作并不是原子的。。 |
6
memedahui 2019-09-27 17:43:36 +08:00
volatile 关键字 只能保证线程可见性,就是指多个线程获取的值是一致的,但是不能保证操作的原子性.
可选方案: 1.AtomicInteger(线程安全类) 2.synchronized wait(锁) 3.ReentrantLock(锁) 4.LockSupport |
7
marlondu 2019-09-27 17:46:34 +08:00
你这是很典型的并发读写操作。主线程负责更改状态,子线程负责读取状态。
如果你希望读取的结果是正确的,也就是满足 i1 * 2 == i2, 那么在读的时候,就不能写,在写的时候就不能读。 所以在读写的地方都需要加锁。同一时刻只能发生一种操作。 volatile 与并发安全无关。 |
8
hyl24 2019-09-27 18:10:13 +08:00
```java
import java.util.Random; /** * <p></p> * * @author Elan Huang * @version v1.0 * @date Create in 2019/9/27 */ public class InvalidCombinationStateDemo { public static void main(String[] args) { CombinationStatTask task = new CombinationStatTask(); Thread thread = new Thread(task); thread.start(); Random random = new Random(); while (true) { int rand = random.nextInt(1000); synchronized (InvalidCombinationStateDemo.class) { InvalidCombinationStateDemo.class.notify(); task.state1 = rand; task.state2 = rand * 2; } } } private static class CombinationStatTask implements Runnable { // 对于组合状态,加 volatile 不能解决问题 int state1; int state2; @Override public void run() { int c = 0; for (long i = 0; ; i++) { synchronized (InvalidCombinationStateDemo.class) { int i1 = state1; int i2 = state2; if (i1 * 2 != i2) { c++; System.err.printf( "Fuck! Got invalid CombinationStat!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n", i + 1, c, (float) c / (i + 1) * 100, i1, i2); } else { // 如果去掉这个输出,则在我的开发机上,发生无效组合的概率由 ~5% 降到 ~0.1% System.out.printf("Emm... %s|%s\n", i1, i2); } try { InvalidCombinationStateDemo.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } ``` |
10
hoperuin 2019-09-27 18:14:34 +08:00
1、把 state1,state2 封装到 Data 对象里面,修改代码如下.
private static class CombinationStatTask implements Runnable { private Data data; public synchronized Data getData(){ return data; } public synchronized void setData(Data data){ this.data = data; } .................. Data data = getData(); int i1 = data.state1; int i2 = data.state2; } |
11
Aruforce 2019-09-27 18:15:50 +08:00
双线程读写变量的问题啊....
要解决问题就是读写互斥就行了... 但是需要借问一下 i1 和 i2 是不是 volatile 的? ```java if(state1 *2 !=state2){ } ``` 改成上面的写法后 !=的概率比原先的写法要低... |
12
hoperuin 2019-09-27 18:16:24 +08:00
经测试,这样也没问题。
private volatile Data data; public Data getData(){ return data; } public void setData(Data data){ this.data = data; } |
13
kidlj 2019-09-27 18:21:57 +08:00
V2EX 评论不支持 markdown 太糟糕了,不知道处于什么考虑。
|
14
lovelife1994 2019-09-27 18:36:36 +08:00 via iPhone
读写的地方都加锁保护复合的状态
|
16
bbao 2019-09-27 19:18:02 +08:00
两个 volatile,两个独立没有相互关系的变量;冲排序这两个变量都没问题,也不满足 happen-before 原则,也发生不了内存屏障。
|