1

I'm using a JDialog instatiated at startup of the application, to show messages several times. Sometimes the dialog and its controls are invisible, but clickable.

The JDialog is instantiated only once and set to visible 'true' each time a message should be shown and then set to visible 'false' till the next message should be shown.

To exlude multithreading related problems, i always use SwingUtilities.invokeLater(...) for ui calls, when a thread creates a message and shows the dialog.

Because its a huge project and my problem isn't related to any specific code, i don't post code but describe the problem. The problems seems not to be reproducible but happens sometimes, so it might be a threading problem despite running each related call on the EDT.

What am I doing wrong?

public class MessageHandler {

private volatile static MessageHandler messageHandler = null;
private List<Message>messages = null;
private volatile WeakReference<MessagesPanelControl> view = null;

private final Object viewSynchronizationObject = new Object();

private MessageHandler() {
    messages = new ArrayList<Message>();
}

public static MessageHandler getInstance() {
    MessageHandler result = messageHandler;
    if (result == null) {
        synchronized (MessageHandler.class) {
            result = messageHandler;
            if (result == null)
                messageHandler = result = new MessageHandler();
        }
    }
    return result;
}


public void registerView(MessagesPanelControl view) {
    this.view = new WeakReference<MessagesPanelControl>(view);
}

public void addMessage(final Message message) {
        synchronized (viewSynchronizationObject) {
           messages.add(message);
           Collections.sort(messages);
           updateView();
        }
}

    private void updateView() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            synchronized (viewSynchronizationObject) {
                if (view == null) {
                    return;
                }
                MessagesPanelControl mpc = view.get();
                if (mpc != null) {
                    mpc.updateView();
                } 
            }
        }
    });
}
}

In the MainFrame class i'm doing this initialization once at startup:

MessagesPanelControl mp = new MessagesPanelControl();
MessageHandler.getInstance().registerView(mp);
LockPane messageBasicPane = new LockPane(this, mp);

And then in different threads this is called to show a message via the MessageHandler Singleton:

MessageHandler.getInstance().addMessage(Message.getSimpleMessage("Error", "Fatal error occured", Message.MessageIcon.ERROR));

I didn't post all details, but all necessary parts to understand the whole problme, hope it makes it more understandable.

The MessagePanelControl (mpc) is a class, that extends JPanel. Its updateView()-method creates the message controlls based on the MessageHandler's message list like buttons, labels and icons. Finally the method sends a Delegate like command to the main frame to show the JDialog containing the MessagePanelControl. Summarized it does:

  • messageList.size()>0: create message panels for each message in list in MessageHandler
  • messageList.size()>0: show JDialog with MessagePanelControl
  • messageList.size()<=0: hide JDialog with MessagePanelControl

    public void updateView() { synchronized (viewMPCSynchronizationObject) { Utils.throwExceptionWhenNotOnEDT();

        JPanel messagesListPanel = new JPanel();
        scrollPane.setViewportView(messagesListPanel);
        scrollPane.setBorder(null);
        messagesListPanel.setLayout(new BoxLayout(messagesListPanel, BoxLayout.Y_AXIS));
        if (MessageHandler.getInstance().getMessages() != null &&  MessageHandler.getInstance().getMessages().size() > 0) {
                      [...]
                      //Create buttons, text icons... for each message
                      [...]
          SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    MainFrame().showMessageBoard();
                }
            }); 
        } else {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    MainFrame().closeMessageBoard();
                }
            });
        }
        repaint();
    }
    

    }

MainFrame:

 //Show Messageboard
 public void showMessageBoard() {
    if (messageBasicPane != null) {
        messageBasicPane.setVisible(true);
        messageBasicPane.repaint();
    }
 }

[...]  
 //Close Messageboard
 public void closeMessageBoard() {
    if (messageBasicPane != null) {
        messageBasicPane.setVisible(false);
    }
 }

This line creates the JDialog, in detail:

[...]
    public LockPane(JFrame parentFrame, JComponent componentToShow, Dimension paneSize, float opacity, ModalityType modality) {
        super(parentFrame, true);
        Utils.throwExceptionWhenNotOnEDT();
        createDialog(paneSize, opacity, modality);
        if (componentToShow != null) {
            add(componentToShow);
        }
        pack();
    }

    private void createDialog(Dimension paneSize, float opacity, ModalityType modality) {
        Utils.throwExceptionWhenNotOnEDT();
        setUndecorated(true);
        setModalityType(modality);
        if (opacity < 1 && opacity >= 0)
            com.sun.awt.AWTUtilities.setWindowOpacity(this, opacity);
        setSize(paneSize);
        setPreferredSize(paneSize);
        setMaximumSize(paneSize);
        setBounds(0, 0, paneSize.width, paneSize.height);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    }   
[...]

A new observation with Java VisualVM is, that the AWT-EventQueue isn't blocked, only sometime there are small periods of 'wait' but nothing blocking. Another strange thing is, that sometimes my JDialog is fully transparent (invisible) and sometimes its white with the desired opacity.

alex
  • 5,516
  • 2
  • 36
  • 60
  • 2
    The problem is that, we actually need to see the code: not your project code but the code of how are you actually viewing and vanishing it. Well, try to make a [SSCCE](http://sscce.org) – Sage Dec 09 '13 at 07:41
  • You are right, i extracted the involved code to a small overview. As far as I see, making an SSCCE is impossible, because the problem is related to call the MessageHandler from different parts/threads. – alex Dec 09 '13 at 08:40
  • 1
    Your code is *not* thread-safe; it doesn’t help doing `synchronized(viewSynchronizationObject)` on the UI thread when other threads call `addMessage` which modifies the message list without any synchronization. *All threads* accessing the same resource must synchronize, otherwise it’s pointless. Note that a UI not paining but still being clickable is an indicator for an exception during the repaint process, in most cases. – Holger Dec 09 '13 at 08:51
  • @Holger: thanks for your comment, you're right, i corrected the addMessage method. Exception do not occur, i always watched the stacktrace, unfortunately no exception there – alex Dec 09 '13 at 08:57
  • @alex: it’s impossible to help further without knowing what `mpc.updateView()` really does. Btw. I’m irritated by the `false` parameter you give to `invokeLater`. – Holger Dec 09 '13 at 14:02
  • @Holger: Sorry for confusion, removed the `boolean` parameter and added an explanation for `updateView()` – alex Dec 09 '13 at 14:37
  • Well your verbal description does not help much for spotting errors. As a recommendation not too far from sage’s answer you should reduce `synchronized` blocks to a minimum (and creating UI components is heavy). So you could create a copy of the messagelist in a synchronized block and do all other work on that copy outside of it. Or use a [concurrent queue](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html) for the messages. But it’s still unclear whether this is the real cause of your problem. – Holger Dec 09 '13 at 14:42
  • If you open a *modal dialog*, this will be really problematic when done in a `synchronized` block. And what do you do (conceptionally) if new messages arrive while the dialog is still open? Seems to be the root of the problems. – Holger Dec 09 '13 at 14:46
  • ok, i added the function code – alex Dec 09 '13 at 14:51
  • Could you please show us the code where 1) the dialog is created and 2) where it is set to visible? When you say the dialog is "clickable" does that mean it generates events? When you say "sometimes", what does that mean? – Radiodef Dec 09 '13 at 15:07
  • @Radiodef: done. *Sometimes* means, it happens without a logic reason (for me) random. I observed, that it happens more often when used with Java SE < 1.7 and when the application has a high workload (e.g. cpu usage high). Further more the problem occurs significant more often when i'm in dubug mode and stopping at some breakpoints, what made me think of threading problems – alex Dec 09 '13 at 15:39
  • What does "clickable" mean specifically? For example, does clicking on a button (or where a button would be) generate an action event? That's what I don't understand. I don't understand how the dialog could be invisible but at the same time respond to input. "Random" does point to a threading issue but "clickable" makes it sound like the EDT is still running. – Radiodef Dec 09 '13 at 15:46
  • Yes as I said, the EDT is _NOT_ blocked, i'm, sure of that because the programm has a checker for that and immediatly reports a frozen EDT. Further more *clickable* really means, i can click where the button is and the programmed MouseEvent code executes. Even the rollover effect (MouseCursor changes) works. After closing the dialog with the invisible button, the programms works further as expected. Only problem is the invisibility of the whole `JDialog` – alex Dec 09 '13 at 15:51
  • Can you show us where you create the dialog originally? Are you doing anything unusual with the UI? Like setting it undecorated, setting its opacity, etc. There are other reasons a window could be invisible, it just seems bizarre that it is intermittent. – Radiodef Dec 09 '13 at 15:53
  • I added the code, and yes i'm doing somethin with opacity :) but i tested it: that seems to have no relation to the problem, without opacity set the problem is still there – alex Dec 09 '13 at 16:07
  • Note that annotating your question with "Editx" every time you edit it is unnecessary; a complete edit history is available for everyone to see [here](http://stackoverflow.com/posts/20465456/revisions). – Robert Harvey Dec 09 '13 at 17:43
  • @robert: thanks for the advice, i corrected my post and added new information. – alex Dec 10 '13 at 07:52

1 Answers1

0

In this function, you are essentially trying to await the Runnable passed to the SwingUtilities.invokeLater which invokeLater submits to the EDT to get executed. The lock that you are holding on viewSynchronizationObject will block EDT if it is locked by other application thread which is actually evident from your code as you have used this variable in several other places.

private void updateView() {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            synchronized (viewSynchronizationObject) {
                if (view == null) {
                    return;
                }
                MessagesPanelControl mpc = view.get();
                if (mpc != null) {
                    mpc.updateView();
                } 
            }
        }


 },false);
}

We should never block EDT from executing it's task other wise our application freezes. Please read my posted answer here for details on how the Swing event and rendering task is performed by EDT and EventQueue.

Although, your application logic is not known to us, you can remove the synchronized (viewSynchronizationObject) {} from invokeLater, instead you can put SwingUtilities.invokeLater(new Runnable() {} inside this synchronized block:

synchronized (viewSynchronizationObject) 
{
   SwingUtilities.invokeLater(new Runnable() { /* your code */ } );
}
Community
  • 1
  • 1
Sage
  • 15,290
  • 3
  • 33
  • 38
  • Ok thanks, thats i great point i missed. I corrected my application and try to reproduce it during the day, because the problem is not reproducible directly. But your point together with the comment of @Holger could solve my problem. Something to add: When the problem occured, all components were clickable+responsive but not visible(so no frozen ui thread) – alex Dec 09 '13 at 10:02
  • @alex, The invisibility shows the sign that EDT is going to get frozen in near future, as you are currently blocking it, if you block it for a little longer it will. – Sage Dec 09 '13 at 10:14
  • Worst suggestion ever. The code executed by the event dispatch thread is *not* thread-safe if you just put `synchronized` around `invokeLater`. In fact, the `synchronized` is completely useless then as `invokeLater` itself does not need a synchronization. What you are recommending is removing the thread-safety entirely. – Holger Dec 09 '13 at 10:25
  • But is this `synchronized` neccessary at all since the `mpc.updateView` method is threadSafe? Further more my understanding of `invokeLater` is/was that the `Runnable` is queued on the EDT, so there is no problem of concurrency. Is that wrong? – alex Dec 09 '13 at 10:54
  • @alex, as i said i don't know your inner communication code. You can't expect me to resolve the issue when i don't know the whether you need a happens-before relationship or not – Sage Dec 09 '13 at 12:08
  • @Holger, make your point clear what makes this suggestion to be thread unsafely. I have told to put the invokeLater inside the synchronized so that his code won't block the EDT at all. Where did you claim that: `The code executed by the event dispatch thread is not thread-safe if you just put synchronized around invokeLater`. Explain your logic. You can't just throw out logic-less statement and mark it as `worst suggestion ever` type comment. – Sage Dec 09 '13 at 12:13
  • There’s nothing that needs to be explained. You suggest to remove the synchronization from the code executed by the event dispatch thread, so there’s no synchronization left in the code executed by the event dispatch thread. *That’s it*. If you insist on your idea, *you* should explain where the thread-safety should come from if you remove the only construct from the code which provided the thread-safety. – Holger Dec 09 '13 at 13:24
  • @Holger, `so there’s no synchronization left in the code executed by the event dispatch thread.`: **YES** and that should be. the thing is that we should not ever synchronize the code which is going to be executed by EDT, because that the lock might block it from getting executed and not blocking the EDT is the recommendation of Swing's single threading rule. IT is a plain clean straight forward statement. I have linked an answer which has in depth discussion. Learn how `EventQueue` works with EDT. – Sage Dec 09 '13 at 13:30
  • putting `synchronized` inside the Runnable of `invokeLater` is not going to provide thread safety at all rather it is going to block EDT, if there is any code segment exist to hold the lock on the Object we are using to synchronize – Sage Dec 09 '13 at 13:32
  • @Sage: creating code which is not thread safe is *no solution*. Of course, blocking should be avoided and critical code should be as short as possible. You are free to suggest one of the existing non-blocking, thread safe alternatives. But just ignoring thread safety is not the answer. – Holger Dec 09 '13 at 13:32
  • Again, you are being unreasonable here throwing out here-there statement. Be straight forward and answer: **why do you think that putting synchronized in EDT is valid approach and why did you think that removing synchronized from EDT is invalid approach**. You are not making sense anyway. There are other experienced answerer in this community too. Ask them – Sage Dec 09 '13 at 13:34
  • If you really understood the answers you have linked you had noticed that all of them use synchronization on the EDT side. They discuss solutions to an issue related to `invokeAndWait` which does not apply here as `invokeLater` behaves differently. None of the answers there advertises removing all thread-safety constructs from the EDT. You are the only one. – Holger Dec 09 '13 at 13:57
  • @Holger, the answer was posted by me. And i wrote an example to strictly proof that what will happen if we access a synchronized block( which includes function) from `SwingUtilities.invokeLater(Runnable)` it will block the EDT too. Run my provided example in your IDE and match with my step-by-step explanation. I got 6+ upvote(maximum) because people were agreeing with me. I have also linked the documentation page too. I wrote the answer for persons like you(including me). So please try to understand how `EventQueue` works which i have explained in the linked answer quite cleanely – Sage Dec 09 '13 at 14:21
  • Your answer there does not tell at any point, that someone should remove all thread safety. Most likely you got far less upvotes if you did. It’s trivial to say that `synchronized` blocks on contention, that’s what it is made for. But for `invokeLater` it does not matter as the caller does not wait for it. If you read the question carefully you did notice that the UI is *not blocked* here as it still reacts on clicks; it just doesn’t paint. By the way, who is “them” that you suggested to ask if the answer is by yourself too? – Holger Dec 09 '13 at 14:36
  • @Hogler, the UI did get blocked. That is why there is no GUI was rendered. I still strongly recommend you to study further without pointless arguing. If you still think that you are right about putting synchronized in `invokeLater` is OK for your own illogical reasoning about thread safety(*Which in-fact is not the reason at all because of it's queue system managed by EventQueue and which will rather block the EDT* )-- then fine. GO ahead. GOOD LUCK TO YOU. – Sage Dec 09 '13 at 14:41
  • I have mentioned about the upvote to emphasize that people are agreeing with me the most. Not to show any gravity. `It’s trivial to say that synchronized blocks on contention, that’s what it is made for. But for invokeLater it does not matter as the caller does not wait for it.`: Again if you are putting syncronized block inside the `Runnable` passed to the `invokeLater` then yes EDT is likely to get blocked if some outside thread hold a lock on that at the time EDT tries to execute it. And hence just to ensure that: You are completely wrong – Sage Dec 09 '13 at 14:46
  • The OP says the controls on the dialog are clickable so there's no evidence at this time that the EDT is being blocked. If the EDT were stuck then no further events would be processed. – Radiodef Dec 09 '13 at 15:02
  • @Radiodef, i hope you understand the fact the difference between Blocked and Frozen. And putting `syncronized` inside the `Runanble` code which is going to be executed by EDT at least is the wrong thing to do. Anyway i am going to flag my answer and the posted answer with link as someone is downvoting me following the discussion with Hogler. I will ask for moderator attention in this matter – Sage Dec 09 '13 at 15:05
  • @Radiodef, please check [the answer here](http://stackoverflow.com/questions/2899682/unresponsive-threading-involving-swing-and-awt-eventqueue/20359861#20359861) i have proved that what will happen if we access a `synncronized` block from the Runnable posted to EDT with `invokeLater`. I have attached an example too. If you run the example you will cleanly see the result. And the fact is that we should not await EDT for any kind of outside task or for mainting happen-before relationship as the internal system will be managed with a `EeventQueue` – Sage Dec 09 '13 at 15:16
  • What's your difference between blocked and frozen? If the EDT gets blocked in one event, no further events will be processed. – Radiodef Dec 09 '13 at 15:17
  • yes the difference is technically implying that: the difference between unresponsive and frozen. It will response a little lately but that doesn't mean that it have got frozen. In my whole answer i haven't claimed that it will solve the OP's issue. He can have several issue and having `synchronized` inside the EDT is one of the major of them. The clean response that i would expect from you: whither you agree that synchronized should(or can) be put inside the EDT or not – Sage Dec 09 '13 at 15:21
  • Hogler first thought that the answer wasn't mine and used that against me. Then when i have said that the answer was actually mine then he reacts as: `oh! but Your answer there does not tell at any point, that someone should remove all thread safety. Most likely you got far less upvotes if you did`. But again he never straightforwardly answer whither we should put synchronized block inside executing code by EDT or not. Because i have cleanely proved that putting synchronized inside EDT executing code block is not going to achieve thread safety rather problematic. Thank you – Sage Dec 09 '13 at 15:49
  • @Radiodef, I am seeing a person doing the wrong thing : and (off-course which will lead to a deadlock too eventually), and i don't see any problem answer that issue. This is the only thing i have seen wrong in his code where any other issues are still hidden. There are lots of answer in StackOVerflow notifying user about the wrong thing. Check some of the page you will find them. Anyway, if you think that you have done the right thing then so be it. Teacher always said: `Think before behaving with someone else: would you expect the same to you?` :) Thank you – Sage Dec 09 '13 at 17:57
  • It’s a pity that you cannot handle critic like an adult but decided to downvote completely unrelated answers, but that don’t get me, I have a real life. Anyway you still didn’t explain how to achieve thread safety without `synchronize` you still seem to deny the need for thread safety at all, that’s not acceptable. And you ignore the fact that your suggestion to remove thread safety from the `Runnable` did *not* solve the issue of the questioner. In fact, you don’t seem to be interested in solving the issue at all. – Holger Dec 11 '13 at 09:48
  • @Holger, i don't think i have downvoted any answer. And i have also said that to `Radiodef`. The fact is that less knowledge is always a danger for anything. You have also showed it in the past while arguing with preferred size in Swing in other post. However, please don't take it personally, knowing less is not a crime, because i am sure you are good in another subject. I have already answer your question. using synchronize with EDT is rather making it thread unsafe as if any other thread going to hold a lock on the object with which we are synchronizing, the EDT is at risk to get blocked. – Sage Dec 11 '13 at 10:04
  • @Holger, I am asking you again to look into Queue management system of `EventQueue` with EDT. Read my linked answer one more time. You will understand the in fact we can put `SwingUtilities.invokeLater(Runnable)` inside a synchronized block but we should not put `syncronized` block or access such from inside the `Runnable` of the `invokeLater` – Sage Dec 11 '13 at 10:05
  • You are still repeating empty statements. But repeating them doesn’t give them more value. The questioner still is accessing an `ArrayList` from multiple threads and is likely to access even more resources concurrently within the code he didn’t show us. You still didn’t give any answer to where the thread-safety for these concurrent access shall come from if he removes the synchronization or why you think you can just ignore thread-safety. Really competent people like Doug Lea have filled entire books on this topic which you declare obsolete… – Holger Dec 13 '13 at 08:53
  • @Holger, read my answer again. I didn't tell him to remove synchronization from any thread but EDT. I specifically told him to remove synchronized locking from the EDT nor from any other thread. You are intentionally playing with words for no reason. I specifically said it many more times. IF you still find a problem, post yourself a question with this specific toipic: `"why should anyone put synchronized locking in the EDT and why should not"`. Ask the question to know the answer: there are lots of people out there to answer your question. A comment place doesn't allow us to discuss in depth – Sage Dec 13 '13 at 09:16