MVC, Multithreading – Why Caller Ensures Thread Safety in GUI Programming

multithreadingmvcuser interface

I have seen, in many places, that it is canonical wisdom1 that it is the responsibility of the caller to ensure you are on the UI thread when updating UI components (specifically, in Java Swing, that you are on the Event Dispatch Thread).

Why is this so? The Event Dispatch Thread is a concern of the view in MVC / MVP / MVVM; to handle it anywhere but the view creates a tight coupling between the view's implementation, and the threading model of that view's implementation.

Specifically, let's say I have an MVC architected application that uses Swing. If the caller is responsible for updating components on the Event Dispatch Thread, then if I try to swap out my Swing View implementation for a JavaFX implementation, I must change all the Presenter / Controller code to use the JavaFX Application thread instead.

So, I suppose I have two questions:

  1. Why is it the caller's responsibility to ensure UI component thread safety? Where is the flaw in my reasoning above?
  2. How can I design my application to have loose coupling of these thread safety concerns, yet still be appropriately thread-safe?

Let me add some MCVE Java code to illustratrate what I mean by "caller responsible" (there are some other good practices here that I'm not doing but I'm trying on purpose to be as minimal as possible):

Caller being responsible:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        view.setData(data);
      }
    });
  }
}
public class View {
  void setData(Data data) {
    component.setText(data.getMessage());
  }
}

View being responsible:

public class Presenter {
  private final View;

  void updateViewWithNewData(final Data data) {
    view.setData(data);
  }
}
public class View {
  void setData(Data data) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        component.setText(data.getMessage());
      }
    });
  }
}

1: The author of that post has the highest tag score in Swing on Stack Overflow. He says this all over the place and I have also seen it being the caller's responsibility in other places, too.

Best Answer

Toward the end of his failed dream essay, Graham Hamilton (a major Java architect) mentions if developers "are to preserve the equivalence with an event queue model, they will need to follow various non-obvious rules," and having a visible and explicit event queue model "seems to help people to more reliably follow the model and thus construct GUI programs that work reliably."

In other words, if you try to put a multithreaded facade on top of an event queue model, the abstraction will occasionally leak in non-obvious ways that are extremely difficult to debug. It seems like it will work on paper, but ends up falling apart in production.

Adding small wrappers around single components probably isn't going to be problematic, like to update a progress bar from a worker thread. If you try to do something more complex that requires multiple locks, it starts getting really difficult to reason about how the multithreaded layer and the event queue layer interact.

Note that these kinds of issues are universal to all GUI toolkits. Presuming an event dispatch model in your presenter/controller isn't tightly coupling you to only one specific GUI toolkit's concurrency model, it's coupling you to all of them. The event queueing interface shouldn't be that hard to abstract.

Related Topic