Java同步列表死锁

时间:2010-04-09 16:15:25

标签: java multithreading deadlock

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.

2 个答案:

答案 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();

我的猜测是死锁可能不会发生。