Dead end with Java synchronized list

From Effective Java 2nd edition item 67 pp. 266-268:

The background thread calls s.removeObserver

which tries to block observers, but it cannot acquire the lock because the main thread already has the lock. All the while, the main thread is waiting for the background thread to finish removing the observer, which explains the deadlock.

I am trying to figure out which threads are stalled in the main method using ThreadMXBean ( Programmatic Deadlock Detection in java ), but why is it not returning stalled threads? I used a new Thread to trigger ThreadMXBean detection.

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


source to share


2 answers


You have a dead end.

However, you don't have a loop, which is what the ThreadMXBean # findDeadlockedThreads method it is looking for indicates. From the javadoc:



Finds looping threads that are stuck waiting to receive object monitors or managed synchronizers. Threads are locked in a loop, waiting for these two types of locks if each thread owns one lock, trying to acquire another lock already held by another thread in the loop.

In this case, the main thread is waiting for "Future" results. Whereas another thread (which does not hold any locks) is waiting for the main thread to release its locks.

+3


source


Are you sure there will be a dead end? Try to run the program with the following changes:

1) Add a log message when deleting an observer:

     executor.submit(new Runnable() { 
        public void run() {
          s.removeObserver(observer);
          System.out.println("Removed myself from observers")
     } }).get();

      



2) Marking the lock detection thread as a daemon:

t.setDaemon(true);
t.start();

      

I guess the deadlock may not happen.

0


source







All Articles