Mik*_*ike 5 java concurrency multithreading thread-safety
这个java代码的线程安全性有什么问题吗?线程1-10通过sample.add()添加数字,而线程11-20调用removeAndDouble()并将结果打印到stdout.我记得在我的脑海里有人说过,以同样的方式在removeAndDouble()中使用它来分配项目可能不是线程安全的.编译器可以优化指令,使它们不按顺序发生.这是这种情况吗?我的removeAndDouble()方法不安全吗?
从这个代码的并发角度来看还有什么问题吗?我试图用java(1.6向上)更好地理解并发性和内存模型.
import java.util.*;
import java.util.concurrent.*;
public class Sample {
private final List<Integer> list = new ArrayList<Integer>();
public void add(Integer o) {
synchronized (list) {
list.add(o);
list.notify();
}
}
public void waitUntilEmpty() {
synchronized (list) {
while (!list.isEmpty()) {
try {
list.wait(10000);
} catch (InterruptedException ex) { }
}
}
}
public void waitUntilNotEmpty() {
synchronized (list) {
while (list.isEmpty()) {
try {
list.wait(10000);
} catch (InterruptedException ex) { }
}
}
}
public Integer removeAndDouble() {
// item declared outside synchronized block
Integer item;
synchronized (list) {
waitUntilNotEmpty();
item = list.remove(0);
}
// Would this ever be anything but that from list.remove(0)?
return Integer.valueOf(item.intValue() * 2);
}
public static void main(String[] args) {
final Sample sample = new Sample();
for (int i = 0; i < 10; i++) {
Thread t = new Thread() {
public void run() {
while (true) {
System.out.println(getName()+" Found: " + sample.removeAndDouble());
}
}
};
t.setName("Consumer-"+i);
t.setDaemon(true);
t.start();
}
final ExecutorService producers = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int j = i * 10000;
Thread t = new Thread() {
public void run() {
for (int c = 0; c < 1000; c++) {
sample.add(j + c);
}
}
};
t.setName("Producer-"+i);
t.setDaemon(false);
producers.execute(t);
}
producers.shutdown();
try {
producers.awaitTermination(600, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.waitUntilEmpty();
System.out.println("Done.");
}
}
Run Code Online (Sandbox Code Playgroud)
它看起来对我来说是安全的.这是我的推理.
每次访问list时都会同步进行.这很棒.即使你拔出的一部分list中item,这item是不能由多个线程访问.
只要您只list在同步时访问,您应该是好的(在您当前的设计中).
您的同步很好,不会导致任何乱序执行问题.
但是,我注意到一些问题.
首先,如果你在in 之后添加一个,你的waitUntilEmpty方法会更加及时.这将消除你的最多10秒延迟.list.notifyAll()list.remove(0)removeAndDoublewait(10000)
其次,你的list.notifyin add(Integer)应该是a notifyAll,因为notify只唤醒一个线程,它可能唤醒一个在里面等待的线程waitUntilEmpty而不是waitUntilNotEmpty.
第三,以上都不是你的应用程序的活跃终结,因为你使用了有限的等待,但如果你做了上面的两个更改,你的应用程序将有更好的线程性能(waitUntilEmpty)和有限的等待变得不必要并且可以变得普通的旧的没有 - arg等待.