From Documentation

Jump to: navigation, search




With the help of server push, you don't have to take care about the problem of multi threads. However, if you would like to handle this job by yourself, you have to conform with the following rules due to the limitations of HTTP.

  • Use the wait method in the Executions class to suspend the event handler itself, after creating a working thread.
  • Because the working thread is not an event listener, it cannot access any components, unless the components don't belong to any desktop. Thus, you might have to pass necessary information manually before starting the working thread.
  • Then, the working thread could crush the information and create components as necessary. Just don't reference any component that belongs to any desktop.
  • Use the notify(Desktop desktop, Object flag) or notifyAll(Desktop desktop, Object flag) method in the Executions class in the working thread to resume the event handler, after the working thread finishes.
  • The resumed event handler won't be executed immediately until another event is sent from the client. To enforce an event to be sent, you could use a timer component (Timer) to fire an event a moment later or periodically. This event listener for this timer could do nothing or update the progress status.

Example: A Working Thread Generates Labels Asynchronously

Assume we want create a label asynchronously. Of course, it is non-sense to do such a little job by applying multi-threading, but you can replace the task with sophisticated one.

 //WorkingThread
 package test;

import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zul.Label;


 public class WorkingThread extends Thread {
     private static int _cnt;
     private Desktop _desktop;
     private Label _label;
     private final Object _mutex = new Integer(0);
 
     /** Called by thread.zul to create a label asynchronously.
    *To create a label, it start a thread, and wait for its completion.
    */
     public static final Label asyncCreate(Desktop desktop)
         throws InterruptedException {
         final WorkingThread worker = new WorkingThread(desktop);
         synchronized (worker._mutex) { //to avoid racing
             worker.start();
             Executions.wait(worker._mutex);
             return worker._label;
         }
     }
     public WorkingThread(Desktop desktop) {
         _desktop = desktop;
     }
     public void run() {
             _label = new Label("Execute "+ ++_cnt);
             synchronized (_mutex) { //to avoid racing
             Executions.notify(_desktop, _mutex);
         }
     }
 }

Then, we have a ZUML page to invoke this working thread in an event listener, say onClick.

<window id="main" title="Working Thread">
    <button label="Start Working Thread">
        <attribute name="onClick">
         timer.start();
         Label label = test.WorkingThread.asyncCreate(desktop);
         main.appendChild(label);
         timer.stop()
        </attribute>
    </button>
    <timer id="timer" running="false" delay="1000" repeats="true"/>
</window>

Notice that we have to use a timer to really resume the suspended the event listener (onClick). It looks odd, but it is a must due to the HTTP limitation: to keep Web page alive at the browser, we have to return the response when the event processing is suspended. Then, when the working thread completes its job and notifies the even listener, the HTTP request was already gone. Therefore, we need a way to 'piggyback' the result, which the timer is used for.

More precisely, when the working thread notifies the event listener to resume, ZK only adds it to a waiting list. And, the listener is really resumed when another HTTP request arrives (in the above example, it is the onTimer event)

In this simple example, we do nothing for the onTimer event. For a sophisticated application, you can use it to send back the progressing status.




Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.