Тупик синхронизированного списка Java - PullRequest
2 голосов
/ 09 апреля 2010

Из Effective Java, 2-е издание, стр. 67, стр. 266-268:

Фоновый поток вызывает s.removeObserver, который пытается заблокировать наблюдателей, но не может получить блокировку, потому что основной поток уже имеет блокировку. Все это время основной поток ожидает, пока фоновый поток завершит удаление наблюдателя, что объясняет тупик.

Я пытаюсь выяснить, какие потоки взаимоблокированы в основном методе, используя ThreadMXBean ( Программное обнаружение взаимоблокировок в java ), но почему он не возвращает заблокированные потоки? Я использовал новый поток для запуска обнаружения 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 ]

3 голосов
/ 10 апреля 2010

У вас тупик.

Однако у вас нет цикла, который метод ThreadMXBean # findDeadlockedThreads сообщает, что ищет. Из Javadoc:

Находит циклы потоков, находящихся в тупике, ожидающих получения мониторов объектов или собственных синхронизаторов. Потоки блокируются в цикле в ожидании блокировки этих двух типов, если каждый поток владеет одной блокировкой при попытке получить другую блокировку, уже удерживаемую другим потоком в цикле.

В этом случае основной поток ожидает результатов Future. В то время как другой поток (который не содержит блокировок) ожидает, пока основной поток освободит свои блокировки.

0 голосов
/ 09 апреля 2010

Вы уверены, что возникла тупиковая ситуация? Попробуйте запустить программу со следующими изменениями:

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

Я предполагаю, что тупика не может быть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...