長操作對于同一個桌面事件是被順序處理的。換個說法,一個事件的處理程序將可以阻塞所有的后續處

理程序。一個長時間被阻塞的請求可能是不可接受的(the time blocking user’s requests might

not be acceptable),如果一個事件的處理將花費大量的時間。象桌面應用,你需要創建一個專用于工

作這種長時間處理的工作線程來減少阻塞時間。

限制于http協議,我們必須符合以下的規則。

1.  在創建了工作線程后,使用wait方法來掛起事件處理進程本身。

2.  因為工作線程不是一個事件監聽器,所以它不能進入任何組件(除非組件不屬于任何桌面)。因此

,在開始工作線程前你可能需要手工通過一些必要的信息。

3.  然后,工作線程可以crush the information和創建組件(若必要的話)。只是不把任何組件關聯

到任何桌面。

4.  在工作線程結束之后,在工作線程中使用類org.zkoss.zk.ui.Executions notify(Desktop

desktop,Object flag)或者notifyAll(Desktop desktop,Object flag)方法來恢復事件處理進程。

5.  直到從客戶另一個事件被發送過來,恢復的事件處理進程才會執行。為了強制發送一個事件,你可

以使用timer組件(org.zkoss.zul.Timer)來觸發一個事件a moment later or periodically.這個

timer的時間監聽器不做任何事情。

例子:工作線程異步的創建標簽

假設我們想異步的創建標簽。當然,這有點象用牛刀來殺雞了,但是我們可以用sophisticated one 來

替換這個任務。

//Working Thread

Package test;

Public class WorkingThread extends Thread

{

private static int _cnt;

private Desktop _desktop;

private Label )label;

private final Object _mutex = new Integer(0);


public static final Label asyncCreate(Desktop desktop)

        throws InterruptedException{

        final WorkingThread worker = new WorkingThread(desktop);

        synchronized(worker._mutex)

        {

               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)

        {

               Executions.notify(_desktop,_mutex);

        }

}

}


然后,我們有一個ZUML頁面在一個事件監聽器里來調用這個工作線程,如在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" runing="false" delay="1000" repeats="true"/>

</window>


注意我們需要使用timer來真正恢復被掛起的事件監聽器(onclick)。這看起來是多余的,但是歸因于

http的限制:為了保持web頁面在瀏覽器中的alive,當事件處理進程被掛起的時候我們必須返回回應。

然后,工作線程完成了工作,喚醒了事件監聽器,http的請求已經gone了。因此,我們需要”piggyback

”這個結果,這就是timer被使用的原因。


更準確的來說,當工作進程喚醒了事件監聽器,ZK只是把它加到一個等待隊列中。當另一個http請求到

達的時候,監聽器才真正的恢復。(在上面的例子中,是onTimer事件)


在這個簡單里例子中,我們對onTimer事件什么都沒做。對于一個sophisticated應用,你可以使用它來

返回處理的狀態。


另一個事例:沒有掛起和恢復

沒有掛起和恢復來執行一個長時間的操作是可能的。當同步代碼對于調試來說太復雜的情況下是有用的


主意很簡單。工作進程在一個臨時空間里保存結果,然后onTimer事件監聽器將結果彈出到桌面。

//WorkingThread2

package test;

public class WorkingThread2 extends Thread

{

private static int _cnt;

private final Desktop _desktop;

private final List _result;

public WorkThread2(Desktop desktop,List result)

{

        _desktop = desktop;

        _result=result;

}

public void run(){

        _result.add(new Label("Execute "+ ++_cnt));

}

}


然后,在onTimer事件監聽器上面追加labels

 

<window id = "main" title="working thread2">

<zscript>

int numpending = 0;

List result=Collections.synchronizedList(new LinkedList());

</zscript>

<button label="start working thread">

        <attribute name="onClick">

               ++numpending;

               timer.start()

               new test.workingthread2(desktop,result).start();

        </attribute>

</button>

<timer id="timer" running="false" delay="1000" repeats="true">

        <attribute name="onTimer">

               while(!result.isEmpty())

               {

                      main.appendChild(result.remove(0));

                      --numpending;

               }

               if(numpending==0)time.stop();

        </attribute>

</timer>

</window>


初始和清除事件處理線程
在處理每個事件之前初始化


一個事件監聽器是在一個事件處理線程中執行的。有時,你不得不在處理所有事件之前初始該線程。


一個典型的例子是初始認證所使用的線程。一些j2ee或者web容器在thread local storage中存儲著認證

信息,因此它們可以在需要時自動進行重復認證。


為了進行事件處理線程的初始化,你需要在web-inf/zk.xml文件注冊一個繼承自

org.zkoss.zk.ui.event.EventThreadInit接口的類。


一旦進行了注冊,在開始一個事件處理線程之前,在主線程中一個指定類的實例就被創建了。然后在處

理其他事情之前,該實例的init方法在事件處理線程的上下文中被調用了。


注意那個構造體和init方法是在不同的線程中被調用的,因此開發者可以從一個線程獲得線程獨立的數

據發送到另一個線程。


下面是jboss的認證機制的例子。在這個例子中,我們在構造體中獲得儲藏在servlet線程中的信息。然

后,我們在init方法調用的時候初始事件處理線程。


import java.security.Principal;

import org.jboss.security.SecurityAssociation;

import org.zkoss.zk.ui.Component;

import org.zkoss.zk.ui.event.Event;

import org.zkoss.zk.ui.event.EventThreadInit;


public class JBossEventThreadInit implements EventThreadInit

{

       private final Principal _principal;

       private final Object _credential;


       public JBossEventThreadInit()

       {

              _principal=SecurityAssociation.getPrincipal();

              _credential=SecurityAssociation.getCredential();

       }


       public void init(Component comp,Event evt)

       {

              SecurityAssociation.setPrincipal(_principal);

              SecurityAssociation.setCredential(_credential);

       }

}


然后在web-inf/zk,xml中,如下進行注冊:

<zk>

       <listener>

              <listener-class>JBossEventThreadInit</listener-class>

       </listener>

</zk>


在處理完每個事件后清除
同樣的,你可能不得不在處理完一個事件后清除一個事件處理進程。


典型的例子是關閉一個transaction,如果它沒有被適當的關閉。


為了清除一個事件處理線程,你需要注冊一個實現org.zkoss.zk.ui.event.EventThreadCleanup接口的

監聽類,然后在web-inf/zk.xml中注冊。


<zk>

       <listener>

              <listener-class>my.MyEventThreadCleanup </listener-class>

       </listener>

</zk>