From Effective Java 2nd edition item 67 page 266-268:
后台线程调用
s.removeObserver
,它试图锁定观察者,但它无法获取锁,因为主线程已经有锁。一直以来,主线程都在等待后台线程完成删除观察者,这解释了死锁。
我试图通过使用ThreadMXBean(Programmatic deadlock detection in java)找出main方法中哪些线程死锁,但为什么它不返回死锁线程? 我使用一个新的Thread来运行ThreadMXBean检测。
public class ObservableSet<E> extends ForwardingSet<E> {
public ObservableSet(Set<E> set) { super(set); }
private final List<SetObserver<E>> observers =
new ArrayList<SetObserver<E>>();
public void addObserver(SetObserver<E> observer) {
synchronized(observers) {
observers.add(observer);
}
}
public boolean removeObserver(SetObserver<E> observer) {
synchronized(observers) {
return observers.remove(observer);
}
}
private void notifyElementAdded(E element) {
synchronized(observers) {
for (SetObserver<E> observer : observers)
observer.added(this, element);
}
}
@Override
public boolean add(E element) {
boolean added = super.add(element); if (added)
notifyElementAdded(element); return added;
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean result = false; for (E element : c)
result|=add(element); //callsnotifyElementAdded
return result;
}
public static void main(String[] args) {
ObservableSet<Integer> set =
new ObservableSet<Integer>(new HashSet<Integer>());
final ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while( true ) {
long [] threadIds = threadMxBean.findDeadlockedThreads();
if( threadIds != null) {
ThreadInfo[] infos = threadMxBean.getThreadInfo(threadIds);
for( ThreadInfo threadInfo : infos) {
StackTraceElement[] stacks = threadInfo.getStackTrace();
for( StackTraceElement stack : stacks ) {
System.out.println(stack.toString());
}
}
}
try {
System.out.println("Sleeping..");
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t.start();
set.addObserver(new SetObserver<Integer>() {
public void added(ObservableSet<Integer> s, Integer e) {
ExecutorService executor = Executors.newSingleThreadExecutor();
final SetObserver<Integer> observer = this; try {
executor.submit(new Runnable() {
public void run() {
s.removeObserver(observer);
} }).get();
} catch (ExecutionException ex) {
throw new AssertionError(ex.getCause());
} catch (InterruptedException ex) {
throw new AssertionError(ex.getCause());
} finally {
executor.shutdown();
}
}
});
for (int i = 0; i < 100; i++)
set.add(i);
}
}
public interface SetObserver<E> {
// Invoked when an element is added to the observable set
void added(ObservableSet<E> set, E element);
}
// ForwardingSet<E> simply wraps another Set and forwards all operations to it.
答案 0 :(得分:3)
你有一个僵局。
但是,您没有循环,这是ThreadMXBean#findDeadlockedThreads方法声明它搜索的循环。来自javadoc:
查找处于死锁中等待获取对象监视器或可拥有同步器的线程的循环。如果每个线程在尝试获取循环中另一个线程已经持有的另一个锁时,每个线程拥有一个锁,则线程在一个循环中死锁,等待这两种类型的锁定。
在这种情况下,主线程正在等待Future的结果。而另一个线程(没有锁定)正在等待主线程释放其锁。
答案 1 :(得分:0)
您确定发生了死锁吗? 尝试运行程序并进行以下更改:
1)删除观察者时添加日志消息:
executor.submit(new Runnable() {
public void run() {
s.removeObserver(observer);
System.out.println("Removed myself from observers")
} }).get();
2)将死锁检测线程标记为守护进程:
t.setDaemon(true);
t.start();
我的猜测是死锁可能不会发生。