我已经阅读了很多有关线程安全的知识。在我的多线程程序的某些部分,我更喜欢尝试不变性。得到不正确的结果后,我注意到我的不可变对象不是线程安全的,尽管它是100%不可变的。如果我错了,请纠正我。
public final class ImmutableGaugeV4 {
private final long max, current;
public ImmutableGaugeV4(final long max) {
this(max, 0);
}
private ImmutableGaugeV4(final long max, final long current) {
this.max = max;
this.current = current;
}
public final ImmutableGaugeV4 increase(final long increament) {
final long c = current;
return new ImmutableGaugeV4(max, c + increament);
}
public final long getCurrent() {
return current;
}
public final long getPerc() {
return current * 100 / max;
}
@Override
public final String toString() {
return "ImmutableGaugeV4 [max=" + max + ", current=" + current + "](" + getPerc() + "%)";
}
}
aaaaa
public class T4 {
public static void main(String[] args) {
new T4().x();
}
ImmutableGaugeV4 g3 = new ImmutableGaugeV4(10000);
private void x() {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 1000; j++) {
g3 = g3.increase(1);
System.out.println(g3);
}
}
}.start();
}
}
}
有时候我得到正确的结果,而且大多数时候我没有得到
ImmutableGaugeV4 [max=10000, current=9994](99%)
ImmutableGaugeV4 [max=10000, current=9995](99%)
ImmutableGaugeV4 [max=10000, current=9996](99%)
ImmutableGaugeV4 [max=10000, current=9997](99%)
这个不可变的对象怎么了?在不使用内部锁的情况下使其成为线程安全的缺少什么?
答案 0 :(得分:1)
都不是
final long c = current;
return new ImmutableGaugeV4(max, c + increament);
也
g3 = g3.increase(1);
是线程安全的。这些复合动作不是原子的。
我建议阅读Brian Goetz的“实践中的Java并发性”:专门讨论复合动作和“发布和逃逸”问题的章节。
答案 1 :(得分:0)
您的问题是您没有对数字变量max和current使用线程安全操作。因此,即使已经更改了许多线程,许多线程也可以从中获得相同的值。
您可以添加同步块来处理对它们的读/写,但是最好的方法是使用线程安全类为您处理这些事情。
如果需要长值,则为AtomicLong。看看它的文档,它具有执行所需操作的方法。
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicLong.html
无论何时使用多线程,都应该使用线程安全的对象,例如Atomic系列,用于地图的ConcurrentHashMap等。
希望有帮助!