Sey*_*emi 5 java multithreading
我正在考虑如何解决两个线程之间的竞争条件,这两个线程试图使用不可变对象写入同一个变量,并且没有帮助任何关键字,例如java中的synchronize(lock)/ volatile.
但我无法弄明白,是否有可能用这样的解决方案解决这个问题?
public class Test {
private static IAmSoImmutable iAmSoImmutable;
private static final Runnable increment1000Times = () -> {
for (int i = 0; i < 1000; i++) {
iAmSoImmutable.increment();
}
};
public static void main(String... args) throws Exception {
for (int i = 0; i < 10; i++) {
iAmSoImmutable = new IAmSoImmutable(0);
Thread t1 = new Thread(increment1000Times);
Thread t2 = new Thread(increment1000Times);
t1.start();
t2.start();
t1.join();
t2.join();
// Prints a different result every time -- why? :
System.out.println(iAmSoImmutable.value);
}
}
public static class IAmSoImmutable {
private int value;
public IAmSoImmutable(int value) {
this.value = value;
}
public IAmSoImmutable increment() {
return new IAmSoImmutable(++value);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您运行此代码,您每次都会得到不同的答案,这意味着竞争条件正在发生.
如果不使用任何存在同步(或易失性)技术,则无法解决竞争条件.那是他们的目的.如果有可能就不需要它们.
更具体地说,您的代码似乎已被破坏.这个方法:
public IAmSoImmutable increment() {
return new IAmSoImmutable(++value);
}
Run Code Online (Sandbox Code Playgroud)
是胡说八道有两个原因:
1)它使类的破坏不变性,因为它改变了对象的变量value.
2)它的结果 - 类的新实例IAmSoImmutable- 从未使用过.
这里的根本问题是您误解了“不变性”的含义。
“不变性”意味着——不能写入。值被创建,但永远不会被修改。
不变性确保不存在竞争条件,因为竞争条件总是由写入引起:两个线程执行彼此不一致的写入,或者一个线程执行写入而另一个线程执行给出不一致结果的读取,或类似的情况。
(警告:即使是不可变对象在构造过程中实际上也是可变的——Java 创建对象,然后填充其字段——因此除了一般情况下不可变之外,您还需要适当地使用final关键字并注意在构造函数中所做的事情。但是,这些都是次要细节。)
有了这个理解,我们就可以回到你最初的那句话:
我正在考虑如何解决两个线程之间的竞争条件,这两个线程尝试使用不可变对象写入同一变量,并且不帮助任何关键字,例如java中的synchronize(lock)/volatile。
这里的问题是,您实际上没有使用不可变对象:您的整个目标是执行写入,而不变性的整个概念是不发生写入。这些不兼容。
也就是说,不变性当然有它的一席之地。您可以拥有不可变的IAmSoImmutable对象,唯一的写入操作是将这些对象相互交换。通过减少您必须担心的写入范围,这有助于简化问题:只有一种写入。但即使是这种写入也需要同步。
这里最好的方法可能是使用AtomicReference<IAmSoImmutable>. 这提供了一种非阻塞的方式来交换IAmSoImmutable-s,同时保证没有写入被默默地丢弃。
(事实上,在您的值只是一个整数的特殊情况下,JDK 提供了AtomicInteger处理必要的比较和交换循环等的线程安全增量。)
| 归档时间: |
|
| 查看次数: |
320 次 |
| 最近记录: |