austin powers austin powers - 9 days ago 10
Java Question

Why does after using multithread appraoch still my GUI gets freezed?

I have create a GUI based app using java based on MVC . the major task of my app is to get some data from internet and starts to parsing it.

for getting data from internet I use following class :

public class WebReader implements Runnable {
WebRecordResult WRResult = new WebRecordResult ();
private void getData(args){
.
.
.
setResult(result);
}

public void run() {
getData(args);
WRResult.setResult(getResult());
}

}


public class WebReaderResult {
private AtomicReference<String> result = new AtomicReference<>("");

public String getResult() {
return result.get();
}

public void setResult(String s) {
result.set(s);
}
}


and inside my controller I have created something like :

WebReaderResult wrr = new WebReaderResult();

ExecutorService executor = Executors.newSingleThreadExecutor();
WebReader wr =new WebReader(args);
Future<?> f1 = executor.submit(wr);
executor.shutdown();
try {
f1.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
wrr.getResult();


still after using this code my GUI gets freezed until data becomes available. I don't want my GUI to be freezed. what shoud I do?

Note : my GUI is swing

Answer

The line

f1.get();

blocks until your thread is done executing. If you execute this line in your UI thread, the UI will freeze, of course. But what are you doing with the result anyway? Without more concrete code, it is hard to give a proper solution to your problem.

** EDIT **

Your controller should execute and handle tasks in it's own thread, and should be completely separated from the UI (for a proper MVC model). Since there are missing information in your code (args, etc.), an example could be :

Results

public class WebReaderResult<V> {
   private AtomicReference<V> ref = new AtomicReference<V>();

   public V getResult() { return ref.get(); }
   public void setResult(V value) { ref.set(value); }
}

Task

public abstract class WebTask<V> {
   private WebReaderResult<V> res = new WebReaderResult<V>();

   public WebReaderResult<V> getWebReaderResult() { return res; }

   // handle task here
   public abstract void handle();
}

Controller

public interface WebControllerCallback<V> {
   public void done(V result);
}

public class WebController {
   static private WebController instance;
   static public WebController getInstance() {
      if (null == instance) instance = new WebController();
      return instance;
   }

   private ExecutorService executor = Executors.newSingleThreadExecutor();

   public void handleTask(WebTask<?> t, WebControllerCallback<?> cb) {
      executor.submit(new Runnable() {
         public void run() {
            t.handle();
            cb.done(t.getWebReaderResult());
         }
      });
   }

   // other methods here
}

UI Code

(in your ActionListener, or any other UI event method)

WebTask<String> task = new WebTask<String>() {
   public void handle() {
      WebReaderResult<String> result = getWebReaderResult();

      // TODO : handle task and set result here result.getResult();
   }
};
WebControllerCallback<String> callback = new WebControllerCallback<String>() {
   public void done(String result) {
      // TODO : update UI here from result value
   }
};

WebController.getInstance().handleTask(task, callback);

** EDIT 2 **

As mentioned by other users, since Java 1.6 there is a class called SwingWorker that you may use to do exactly that, but with a lot less code. Just put something like this wherever required in your UI event (or create a separate class to implement the methods) :

new SwingWorker<String, Object>() {
   @Override
   protected String doInBackground() throws Exception {
      // TODO : process result

      return "some result";
   }

   protected void done() {
      // TODO : refresh UI with the value of this.get(); which return a String
   }
}.execute();

This last edit is not as clear as the first example, however the SwingWorker class does offer what you are looking for. Now, all you need is to delegate the processing done in both doInBackground and done using your WebReader class (ie. by calling wr.run(); in the doInBackground, or something.). In any case, I believe you have plenty to go on and about with this now.