java为什么同步在以下代码中不起作用?
这段代码有时会引发异常,即使我在run方法的synchronized
块中使用了synchronized
方法removeFirst
,我还是在synchronizedList
上添加和删除元素
public class NameDropper extends Thread {
private NameList n1;
public NameDropper(List list) {
this.n1 = new NameList(list);
}
public static void main(String[] args) {
List l = Collections.synchronizedList(new LinkedList());
NameDropper n = new NameDropper(l);
n.n1.add("Ozymandias");
Thread[] t = new NameDropper[10];
for (int i = 1; i <= 10; i++) {
t[i - 1] = new NameDropper(l);
t[i - 1].setName("T" + Integer.toString(i - 1));
t[i - 1].start();
}
}
public void run() {
synchronized (this) {
try {
Thread.sleep(50);
String name = n1.removeFirst();
System.out.println(Thread.currentThread().getName() + ": "
+ name);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class NameList {
private List names = null;
public NameList(List list) {
this.names = list;
}
public synchronized void add(String name) {
names.add(name);
}
public synchronized String removeFirst() {
if (names.size() > 0)
return (String) names.remove(0);
else
return null;
}
}
它抛出的异常是:
T1: Ozymandias
T2: null
*Exception in thread "T3" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.LinkedList.entry(Unknown Source)
at java.util.LinkedList.remove(Unknown Source)
at java.util.Collections$SynchronizedList.remove(Unknown Source)
at NameList.removeFirst(NameDropper.java:57)*
T0: null
T8: null
*at NameDropper.run(NameDropper.java:33)*
T6: null
T4: null
T9: null
T7: null
T5: null
# 1 楼答案
正如其他人所说,名单没有被分享。以下是一种只需最少重新编码即可修复代码的方法(还有其他方法):
更改构造函数以获取名称列表(而不是列表)
在当前创建列表的位置创建名称列表
# 2 楼答案
即使您正在使用
Collections.synchronizedList
,代码中也存在竞争条件下面是代码中种族编码的示例
因为您正在为每个线程
which shares single instance of List
创建NameDropper
实例,所以您有这个竞争条件您可以做的是为每个
NameDropper
创建单独的列表这样,每个
NameDropper
都有自己的List
实例# 3 楼答案
正如其他人所指出的,你有一个竞争条件,因为你所有的线程都是同步的。您需要一个公共对象来进行同步
我建议您在列表上同步。这将意味着争夺同一列表的任何实例都会被彼此阻止,而任何未被阻止的线程都不会被阻止。添加和删除方法应为:
# 4 楼答案
您正在为每个线程创建一个新的
NameDropper
实例因此,
synchronized
方法实际上并没有锁定,因为每个实例从来没有被两个线程使用过# 5 楼答案
一般来说:
1) 由于每次都要创建一个新的类实例,所以基本上没有“公共”对象供所有线程锁定。你应该定义如下:
而
synchronize
在这个对象上2)IMHO最好实现
Runnable
,而不是扩展Thread