此代码有时会抛出异常,即使我在synchronized
运行方法块中使用了removeFirst
方法synchronized
,我也会在synchronizedList
上添加和删除元素。< / p>
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
答案 0 :(得分:5)
您正在为每个帖子创建一个新的NameDropper
实例
因此,synchronized
方法实际上并不锁定,因为两个线程从不使用每个实例。
答案 1 :(得分:2)
正如其他人所指出的,你有一个竞争条件,因为所有的线程都是自己同步的。您需要一个公共对象来同步。
我建议您在列表本身上进行同步。这意味着任何争用相同列表的实例都会相互阻塞,并且不会阻止任何不会被阻止的线程。您的添加和删除方法应为:
public void add(String name) {
synchronized (name) {
names.add(name);
}
}
public String removeFirst() {
synchronized (name) {
if (names.size() > 0)
return (String) names.remove(0);
else
return null;
}
}
答案 2 :(得分:1)
即使您使用Collections.synchronizedList
,代码中也存在争用条件。
以下是您的代码中的竞赛编码示例。
lock(NameDropper[0]) lock(NameDropper[1])
names.size() > 0 is true names.size() > 0 is true
names.remove(0)
names.remove(0) <--- Error here.
由于您为每个线程NameDropper
创建which shares single instance of List
实例,因此您有这种竞争条件。
您可以为每个NameDropper
List l1 = Collections.synchronizedList(new LinkedList());
t[i - 1] = new NameDropper(l1);
这样每个NameDropper
都有自己的List
实例。
答案 3 :(得分:1)
一般来说:
1)由于每次都在创建类的新实例,因此基本上没有“公共”对象可供所有线程锁定。你应该定义类似的东西:
static final Object lock = new Object();
取而代之的是和synchronize
。
2)恕我直言,最好实施Runnable
而不是扩展Thread
。
答案 4 :(得分:0)
正如其他人所说,NameList未被共享。这是一种最少重新编码来修复代码的方法(还有其他方法):
更改构造函数以获取NameList(而不是List)。
public NameDropper(NameList list) {
this.n1 = list;
}
创建您当前正在创建List的NameList。
NameList l = new NameList(Collections.synchronizedList(new LinkedList()));