1

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

Matthieu
  • 2,736
  • 4
  • 57
  • 87
Vahid Hashemi
  • 5,182
  • 10
  • 58
  • 88
  • are you using what to render the GUI ? if it's SWING you need to take a look at http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html – BigMike Jan 02 '12 at 16:01
  • yeah my GUI is swing , can u give me a lucid example? cheers – Vahid Hashemi Jan 02 '12 at 16:02
  • 2
    Why are you using an Executor? In your last posting (http://stackoverflow.com/questions/8695307/how-to-make-my-program-to-wait-for-a-particular-thread-and-also-not-affecting-my) everyone voted for the Swing Worker solution. You where given a link to the tutorial that contained working examples of using a Swing Worker. In this posting again people of recommending a Swing Worker. Quit wasting peoples time and try listening to advice when you ask for it! – camickr Jan 02 '12 at 16:15
  • the reason is I want to create more abstract MVC app and swingworker will restrict me cause again I should create a Model inherited form swingworker and a controller which taking care of it.thought in this way I can ditch it :P but I waste like 3 hours of my time – Vahid Hashemi Jan 02 '12 at 16:20
  • Swing worker works within a MVC design. A Swing worker is used to execute long running tasks and then publish the results. When you publish the results you update the model, which in turn will cause the the view to be painted, which is how MVC works in Swing. – camickr Jan 02 '12 at 16:25
  • I don't see a problem at all. Just write your own thread and run it throu a SwingWorker in the Swing View. – BigMike Jan 02 '12 at 16:29
  • @austin powers SwingWorker invoked from Executor is nice way how to do, all answers in this thread missed very important about EDT and wrapping output to the GUI into invokeLater, because not all Swing methods are thread_sate, please follows suggestion by (@mre) – mKorbel Jan 02 '12 at 17:31

5 Answers5

4

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.

Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • the whole program is about 2000 lines .... as I describe it above I'll parse the data and draw plot based on acquired data , GUI will call the controller as I provided the skeleton above and ctrl--->model--->process data--->make some result--->ctl will get the data and show it inside the view – Vahid Hashemi Jan 02 '12 at 16:12
  • 1
    Your "controller" is executed in your UI thread then, which is not the proper way of handling your tasks. I'll update my answer to an example on how you could multi-thread your application. – Yanick Rochon Jan 02 '12 at 16:20
  • Why are you inventing your own 'callback stategy"? SwingWorker already supports this. – camickr Jan 02 '12 at 19:32
  • because I haven't really looked at the Java implementation since 1.6. The last `SwingWorker` implementation I've seen was a deadlock-thread-safe-glorified class (here : http://java.sun.com/products/jfc/tsc/articles/threads/src/SwingWorker.java) So, yes, the 1.6 `SwingWorker` class should be used instead then. Though there's nothing with doing something from scratch, especially when that something is simple, as a learning exercise :) – Yanick Rochon Jan 02 '12 at 21:04
  • 1
    It is not that simple especially to someone who asks this type of question because they don't understand the basics. As you said the first implementation had its problems. That is why you should not always reinvent the wheel. Anybody else who reads the code won't understand why the poster is doing what they are doing when there is a standard JDK solution to the problem. This answer really deserves a down vote! – camickr Jan 02 '12 at 23:16
  • well, then, let me append an edit for `SwingWorker` if it can clear things up for other people... – Yanick Rochon Jan 03 '12 at 00:49
3

Swing is single-threaded. This thread's name is the Event Dispatch Thread (EDT). A Swing application will appear to freeze when a long-running task occurs in the EDT. In order to avoid this, utilities such as, javax.swing.Timer, javax.swing.SwingUtilities, and javax.swing.SwingWorker<T,V> were developed. These utility classes ensure that any action that modifies a Swing component occurs in the EDT and that all long-running tasks occur in a separate thread (aka a background thread).

I suggest you review your code and further investigate which methods may block a thread (this is usually documented). After that, you can check whether these methods occur in the EDT by invoking SwingUtilities.isEventDispatchThread() prior to their execution (this is just for verification).

mre
  • 43,520
  • 33
  • 120
  • 170
1

what shoud I do?

Read the documentation. :)

Seriously:

The JavaDoc on Future#get() says this (emphasis by me):

Waits if necessary for the computation to complete, and then retrieves its result.

Thus you might try calling f1.isDone() every now and so often until it returns true, in which case you can call get().

Thomas
  • 87,414
  • 12
  • 119
  • 157
0

The code f1.get() is a blocking call.

1) You should break down your task into granular units and then probably use more than one threads.

2) Also, use callback mechanism instead of blocking, your task should accept a completion listener and notify the interested observers of task completion rather than making the blocking call.

Scorpion
  • 3,938
  • 24
  • 37
0

Straight from official javadocs (with some hopefully helpfull comments):

final JLabel label;
   class MeaningOfLifeFinder extends SwingWorker<String, Object> {
       @Override
       public String doInBackground() {
           // In here put the time consuming task
           return findTheMeaningOfLife();
       }

       @Override
       protected void done() {
           // In here you'd be in UI thread, so you can actually render some text in a JLabel
           try { 
               label.setText(get());
           } catch (Exception ignore) {
           }
       }
   }

And finally here's the usage:

   (new MeaningOfLifeFinder()).execute();
BigMike
  • 6,683
  • 1
  • 23
  • 24