[ Java 并发] 老哥们,求救啊

2019-09-27 17:17:42 +08:00
 JsonTu

昨天闲来无事做,在 GitHub 上看到了并发的的例子,其中有个一直不知道怎么解决。感觉都快魔障了

地址: https://github.com/oldratlee/fucking-java-concurrency/blob/master/src/main/java/com/oldratlee/fucking/concurrency/InvalidCombinationStateDemo.java

还望各位大佬不吝赐教

2821 次点击
所在节点    程序员
21 条回复
Banxiaozhuan
2019-09-27 17:23:12 +08:00
volatile int state1;
volatile int state2;
这两个变量并不是同步更新的, 他们之间的更新可以发生并发问题。
JsonTu
2019-09-27 17:25:29 +08:00
@Banxiaozhuan 菜鸟一枚,只会加 volatile、synchronized。我写个 synchronized setState(int state1, int state2),也是不行,把想到的,都试过一边,也不行
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);
}
}
}
}
Yuicon
2019-09-27 17:34:33 +08:00
虽然排版没了 但是这样就行了 性能后面优化
LeeSeoung
2019-09-27 17:36:34 +08:00
task.state1 = rand;
task.state2 = rand * 2;
可能执行完第一行代码 然后就去执行你 task 里的判断了,这两个操作并不是原子的。。
memedahui
2019-09-27 17:43:36 +08:00
volatile 关键字 只能保证线程可见性,就是指多个线程获取的值是一致的,但是不能保证操作的原子性.
可选方案:
1.AtomicInteger(线程安全类)
2.synchronized wait(锁)
3.ReentrantLock(锁)
4.LockSupport
marlondu
2019-09-27 17:46:34 +08:00
你这是很典型的并发读写操作。主线程负责更改状态,子线程负责读取状态。
如果你希望读取的结果是正确的,也就是满足 i1 * 2 == i2, 那么在读的时候,就不能写,在写的时候就不能读。
所以在读写的地方都需要加锁。同一时刻只能发生一种操作。

volatile 与并发安全无关。
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();
}
}
}
}
}
}
```
JsonTu
2019-09-27 18:12:35 +08:00
@Yuicon #3 感谢,我稍后试试
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;
}
Aruforce
2019-09-27 18:15:50 +08:00
双线程读写变量的问题啊....
要解决问题就是读写互斥就行了...


但是需要借问一下 i1 和 i2 是不是 volatile 的?


```java
if(state1 *2 !=state2){
}
```

改成上面的写法后 !=的概率比原先的写法要低...
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;
}
kidlj
2019-09-27 18:21:57 +08:00
V2EX 评论不支持 markdown 太糟糕了,不知道处于什么考虑。
lovelife1994
2019-09-27 18:36:36 +08:00
读写的地方都加锁保护复合的状态
Aruforce
2019-09-27 18:49:37 +08:00
@Aruforce fix 了不是
bbao
2019-09-27 19:18:02 +08:00
两个 volatile,两个独立没有相互关系的变量;冲排序这两个变量都没问题,也不满足 happen-before 原则,也发生不了内存屏障。
JsonTu
2019-09-29 14:12:53 +08:00
@Yuicon 大佬,这样写还是有问题,出错保持在 0.045%左右
JsonTu
2019-09-29 14:13:55 +08:00
@LeeSeoung
@marlondu
道理都懂,原理不懂,不会写啊。。。
JsonTu
2019-09-29 14:16:35 +08:00
@Yuicon 不好意思,上面的入参我没改,导致出问题。您的代码是正确的
JsonTu
2019-09-29 14:21:34 +08:00
@hyl24 感谢,您的代码是正确的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/604889

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX