寒假我就上網(wǎng)很少了,真的非常不好意思.eclipse中的線程是開發(fā)eclipse插件中一個比較重要的基礎(chǔ),而很多書上都沒有說,我最后抽時間寫了這篇入門文章,希望對大家有幫助.
我先大致講一講GUI程序中的線程.
? ? 雖然各個操作系統(tǒng)之間的線程機制是不一樣的,但是大致是相同的.當(dāng)用戶使用GUI程序時,如果點鼠標(biāo)或按下鍵盤上的鍵等時,操作系統(tǒng)會產(chǎn)生對應(yīng)的GUI事件,它來決定哪個窗口或程序來接受每一個事件并且放到程序的事件隊列中.
? ? 任何GUI程序的底層結(jié)構(gòu)就是一個事件循環(huán).程序首先初始化事件循環(huán),并開始循環(huán),這個循環(huán)會從事件隊列依次接收GUI事件并一一做出相應(yīng)的反應(yīng).程序應(yīng)該對事件做出快速的反應(yīng)使程序一直對用戶有響應(yīng),舉個例子,用戶點了一下程序里的一個按鈕結(jié)果程序就沒反應(yīng)了,那么這個程序應(yīng)該算是一個失敗的程序吧.
? ? 如果某個UI事件引發(fā)了某個需要長時間的事務(wù),那么應(yīng)該把它放到一個另外的單獨的線程中,這樣程序的那個事件循環(huán)就能夠馬上回來響應(yīng)用戶的下一個操作.線程是非常復(fù)雜的一個主題,如果處理的不好很容易造成死鎖等很糟糕的情況.
? ? 還好,eclipse為我們開發(fā)插件提供了一個方便的UI線程包,大大的簡化了很多底層復(fù)雜的東西.先看看幾個簡單的概念.
1.SWT UI線程
SWT用的是操作系統(tǒng)直接支持的線程模式,程序會在主程序里運行一個時間循環(huán)并依次在這個線程里響應(yīng)事件.看下面這段代碼,UI線程就是創(chuàng)建Display的那個線程.
public static void main (String [] args) {
? ? Display display = new Display ();
? ? Shell shell = new Shell (display);
? ? shell.open ();
? ? // 開始事件循環(huán)
? ? // 關(guān)掉窗口后
? ? while (!shell.isDisposed ()) {
? ? ? if (!display.readAndDispatch ())
? ? ? ? display.sleep ();
? ? }
? ? display.dispose ();
? }
簡單的小程序里,一個UI線程就能夠滿足需要了.
但如果是長時間的操作,你就最好不要用UI線程來做這些事,可以交給Job去做.它其實就是另外啟動的線程,也就是等會我要說的非UI線程.
2.Job
Job類由org.eclipse.core.runtime插件提供.它能夠讓客戶程序員輕松的在另外的線程中執(zhí)行代碼.
看一個小例子
Job job = new Job("My First Job") {
? protected IStatus run(IProgressMonitor monitor) {
? ? ? System.out.println("Hello World (from a background job)");
? ? ? return Status.OK_STATUS;
? ? }
? };
job.setPriority(Job.SHORT);
job.schedule(); // start as soon as possible
Job的默認(rèn)優(yōu)先級是Job.Long,這里例子中的優(yōu)先級要比它高.
只要調(diào)用Job#schedule(),它就會盡快在另外的線程中運行run()中的代碼.
再看一個小例子:
final Job job = new Job("Long Running Job") {
? ? protected IStatus run(IProgressMonitor monitor) {
? ? ? try {
? ? ? ? while(hasMoreWorkToDo()) {
? ? ? ? ? // do some work
? ? ? ? ? // ...
? ? ? ? if (monitor.isCanceled()) return Status.CANCEL_STATUS;
? ? ? ? }
? ? ? ? return Status.OK_STATUS;
? ? ? } finally {
? ? ? ? schedule(60000); // start again in an hour
? ? ? }
? ? }
? };
job.addJobChangeListener(new JobChangeAdapter() {
? ? public void done(IJobChangeEvent event) {
? ? if (event.getResult().isOK())
? ? ? postMessage("Job completed successfully");
? ? ? else
? ? ? ? postError("Job did not complete successfully");
? ? }
? });
job.setSystem(true);
? job.schedule(); // start as soon as possible
monitor 是一個進度顯示條,它會在運行job時自動顯示,如果任務(wù)成功運行完成,返回Status.OK_STATUS,如果中途被用戶在進度顯示條那里中斷,就返回Status.CANCEL_STATUS.上面schedule(60000);它是讓job每過1小時就自動運行,Job又一個非常強大的功能.
然后后面是可以給job添加監(jiān)聽器.
job.setSystem(true);這一句是把這個job設(shè)置為系統(tǒng)級別的.如果調(diào)用setUser(true),那么就被定義為用戶級別的,用戶級別和默認(rèn)級別的job
? ? 在運行時會以UI形式反映出來,如果是用戶job,那么會彈出一個進度顯示窗口,能讓用戶選擇在后臺里運行.
下圖是一個job自動運行時的效果:

再介紹job常常用到的一個方法Job#join().
系統(tǒng)調(diào)用到某個job,調(diào)用它的run()方法:
再看下面這個例子:
? class TrivialJob extends Job {
? ? public TrivialJob() {
? ? ? super("Trivial Job");
? ? }
? ? public IStatus run(IProgressMonitor monitor) {
? ? ? System.out.println("This is a job");
? ? ? return Status.OK_STATUS;
? ? }
? }
? job的創(chuàng)建和計劃如下所示:
? TrivialJob job = new TrivialJob();
? System.out.println("About to schedule a job");
? job.schedule();
? System.out.println("Finished scheduling a job");
? 他們的執(zhí)行是和時間沒關(guān)系的,輸出可能如下:
? About to schedule a job
? This is a job
? Finished scheduling a job
也可能是:
? About to schedule a job
? Finished scheduling a job
? This is a job
?
? 如果希望某個job運行完成后在繼續(xù)時,可以使用join()方法.
? join()會一直阻塞到該job運行完.
例子:
? TrivialJob job = new TrivialJob();
? System.out.println("About to schedule a job");
? job.schedule();
? job.join();
? if (job.getResult().isOk())
? ? System.out.println("Job completed with success");
? else
? ? System.out.println("Job did not complete successfully");
上面的代碼執(zhí)行后,輸出應(yīng)該就是這樣:
? About to schedule a job
? This is a job
? Job completed with success
Job的功能是很強大的,還有很多功能我以后會介紹,也可以查閱官方幫助文檔.這里先把幾個常用的問題解決掉.
參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/runtime_jobs.htm
3.如果在Job中加上改變UI的代碼就會失敗.
原因如下:
如果是在非UI線程中調(diào)用UI,SWT就會拋出一個SWTException.
要在一個非UI線程改變UI的話有幾種技術(shù):
第一種,用:
Display#syncExec(Runnable)或
Diaplay#asyncExec(Runnable)
第二種:
已經(jīng)開發(fā)了另外一種Job,就是UIJob,可以直接在它里面運行改變UI的代碼,其實它就是在SWT的asyncExec()方法里運行的.所有繼承UIJob的類應(yīng)
該覆寫runInUIThread方法而不是run方法.
3.關(guān)于進度顯示
在Jface中:
org.eclipse.jface.operations包定義了一些接口用來在進度條下運行長時間的任務(wù).
可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/jface_operations.htm
在eclipse插件和RCP開發(fā)中:
用戶級別的job是互操作性最強的,它不僅能夠讓用戶用Cancel鍵取消job,而且可以在Detail中展示具體情況,但是注意:
Detail只會在下面兩種方法中出現(xiàn):
IProgressService#busyCursorWhile或
IProgressService#runInUI
1)IProgressService#busyCursorWhile的用法例子:
注意這里的run()中做些和UI無關(guān)的事.
IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
? progressService.busyCursorWhile(new IRunnableWithProgress(){
? ? public void run(IProgressMonitor monitor) {
? ? ? //do non-UI work
? ? }
? });
效果:

2)IProgressService#runInUI的用法例子:
注意這里的run()中可以做些和UI有關(guān)的事.
progressService.runInUI(
? ? PlatformUI.getWorkbench().getProgressService(),
? ? new IRunnableWithProgress() {
? ? ? public void run(IProgressMonitor monitor) {
? ? ? ? //do UI work
? ? ? }
? ? },
? ? Platform.getWorkspace().getRoot());
效果:

這里最后一個參數(shù)可以是null,或者是這個操作的規(guī)則,在這里我們是設(shè)定運行這個UI操作時鎖定工作臺.
更加具體的可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/workbench_jobs.htm
另外,有少數(shù)時候,我們不想彈出一個進度條窗口,而是只在最底下的狀態(tài)欄顯示就可以了,很簡單,寫自己的Job類時,在構(gòu)造方法里加上一句:
setUser(false);就可以了.
我先大致講一講GUI程序中的線程.
? ? 雖然各個操作系統(tǒng)之間的線程機制是不一樣的,但是大致是相同的.當(dāng)用戶使用GUI程序時,如果點鼠標(biāo)或按下鍵盤上的鍵等時,操作系統(tǒng)會產(chǎn)生對應(yīng)的GUI事件,它來決定哪個窗口或程序來接受每一個事件并且放到程序的事件隊列中.
? ? 任何GUI程序的底層結(jié)構(gòu)就是一個事件循環(huán).程序首先初始化事件循環(huán),并開始循環(huán),這個循環(huán)會從事件隊列依次接收GUI事件并一一做出相應(yīng)的反應(yīng).程序應(yīng)該對事件做出快速的反應(yīng)使程序一直對用戶有響應(yīng),舉個例子,用戶點了一下程序里的一個按鈕結(jié)果程序就沒反應(yīng)了,那么這個程序應(yīng)該算是一個失敗的程序吧.
? ? 如果某個UI事件引發(fā)了某個需要長時間的事務(wù),那么應(yīng)該把它放到一個另外的單獨的線程中,這樣程序的那個事件循環(huán)就能夠馬上回來響應(yīng)用戶的下一個操作.線程是非常復(fù)雜的一個主題,如果處理的不好很容易造成死鎖等很糟糕的情況.
? ? 還好,eclipse為我們開發(fā)插件提供了一個方便的UI線程包,大大的簡化了很多底層復(fù)雜的東西.先看看幾個簡單的概念.
1.SWT UI線程
SWT用的是操作系統(tǒng)直接支持的線程模式,程序會在主程序里運行一個時間循環(huán)并依次在這個線程里響應(yīng)事件.看下面這段代碼,UI線程就是創(chuàng)建Display的那個線程.
public static void main (String [] args) {
? ? Display display = new Display ();
? ? Shell shell = new Shell (display);
? ? shell.open ();
? ? // 開始事件循環(huán)
? ? // 關(guān)掉窗口后
? ? while (!shell.isDisposed ()) {
? ? ? if (!display.readAndDispatch ())
? ? ? ? display.sleep ();
? ? }
? ? display.dispose ();
? }
簡單的小程序里,一個UI線程就能夠滿足需要了.
但如果是長時間的操作,你就最好不要用UI線程來做這些事,可以交給Job去做.它其實就是另外啟動的線程,也就是等會我要說的非UI線程.
2.Job
Job類由org.eclipse.core.runtime插件提供.它能夠讓客戶程序員輕松的在另外的線程中執(zhí)行代碼.
看一個小例子
Job job = new Job("My First Job") {
? protected IStatus run(IProgressMonitor monitor) {
? ? ? System.out.println("Hello World (from a background job)");
? ? ? return Status.OK_STATUS;
? ? }
? };
job.setPriority(Job.SHORT);
job.schedule(); // start as soon as possible
Job的默認(rèn)優(yōu)先級是Job.Long,這里例子中的優(yōu)先級要比它高.
只要調(diào)用Job#schedule(),它就會盡快在另外的線程中運行run()中的代碼.
再看一個小例子:
final Job job = new Job("Long Running Job") {
? ? protected IStatus run(IProgressMonitor monitor) {
? ? ? try {
? ? ? ? while(hasMoreWorkToDo()) {
? ? ? ? ? // do some work
? ? ? ? ? // ...
? ? ? ? if (monitor.isCanceled()) return Status.CANCEL_STATUS;
? ? ? ? }
? ? ? ? return Status.OK_STATUS;
? ? ? } finally {
? ? ? ? schedule(60000); // start again in an hour
? ? ? }
? ? }
? };
job.addJobChangeListener(new JobChangeAdapter() {
? ? public void done(IJobChangeEvent event) {
? ? if (event.getResult().isOK())
? ? ? postMessage("Job completed successfully");
? ? ? else
? ? ? ? postError("Job did not complete successfully");
? ? }
? });
job.setSystem(true);
? job.schedule(); // start as soon as possible
monitor 是一個進度顯示條,它會在運行job時自動顯示,如果任務(wù)成功運行完成,返回Status.OK_STATUS,如果中途被用戶在進度顯示條那里中斷,就返回Status.CANCEL_STATUS.上面schedule(60000);它是讓job每過1小時就自動運行,Job又一個非常強大的功能.
然后后面是可以給job添加監(jiān)聽器.
job.setSystem(true);這一句是把這個job設(shè)置為系統(tǒng)級別的.如果調(diào)用setUser(true),那么就被定義為用戶級別的,用戶級別和默認(rèn)級別的job
? ? 在運行時會以UI形式反映出來,如果是用戶job,那么會彈出一個進度顯示窗口,能讓用戶選擇在后臺里運行.
下圖是一個job自動運行時的效果:

再介紹job常常用到的一個方法Job#join().
系統(tǒng)調(diào)用到某個job,調(diào)用它的run()方法:
再看下面這個例子:
? class TrivialJob extends Job {
? ? public TrivialJob() {
? ? ? super("Trivial Job");
? ? }
? ? public IStatus run(IProgressMonitor monitor) {
? ? ? System.out.println("This is a job");
? ? ? return Status.OK_STATUS;
? ? }
? }
? job的創(chuàng)建和計劃如下所示:
? TrivialJob job = new TrivialJob();
? System.out.println("About to schedule a job");
? job.schedule();
? System.out.println("Finished scheduling a job");
? 他們的執(zhí)行是和時間沒關(guān)系的,輸出可能如下:
? About to schedule a job
? This is a job
? Finished scheduling a job
也可能是:
? About to schedule a job
? Finished scheduling a job
? This is a job
?
? 如果希望某個job運行完成后在繼續(xù)時,可以使用join()方法.
? join()會一直阻塞到該job運行完.
例子:
? TrivialJob job = new TrivialJob();
? System.out.println("About to schedule a job");
? job.schedule();
? job.join();
? if (job.getResult().isOk())
? ? System.out.println("Job completed with success");
? else
? ? System.out.println("Job did not complete successfully");
上面的代碼執(zhí)行后,輸出應(yīng)該就是這樣:
? About to schedule a job
? This is a job
? Job completed with success
Job的功能是很強大的,還有很多功能我以后會介紹,也可以查閱官方幫助文檔.這里先把幾個常用的問題解決掉.
參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/runtime_jobs.htm
3.如果在Job中加上改變UI的代碼就會失敗.
原因如下:
如果是在非UI線程中調(diào)用UI,SWT就會拋出一個SWTException.
要在一個非UI線程改變UI的話有幾種技術(shù):
第一種,用:
Display#syncExec(Runnable)或
Diaplay#asyncExec(Runnable)
第二種:
已經(jīng)開發(fā)了另外一種Job,就是UIJob,可以直接在它里面運行改變UI的代碼,其實它就是在SWT的asyncExec()方法里運行的.所有繼承UIJob的類應(yīng)
該覆寫runInUIThread方法而不是run方法.
3.關(guān)于進度顯示
在Jface中:
org.eclipse.jface.operations包定義了一些接口用來在進度條下運行長時間的任務(wù).
可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/jface_operations.htm
在eclipse插件和RCP開發(fā)中:
用戶級別的job是互操作性最強的,它不僅能夠讓用戶用Cancel鍵取消job,而且可以在Detail中展示具體情況,但是注意:
Detail只會在下面兩種方法中出現(xiàn):
IProgressService#busyCursorWhile或
IProgressService#runInUI
1)IProgressService#busyCursorWhile的用法例子:
注意這里的run()中做些和UI無關(guān)的事.
IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
? progressService.busyCursorWhile(new IRunnableWithProgress(){
? ? public void run(IProgressMonitor monitor) {
? ? ? //do non-UI work
? ? }
? });
效果:

2)IProgressService#runInUI的用法例子:
注意這里的run()中可以做些和UI有關(guān)的事.
progressService.runInUI(
? ? PlatformUI.getWorkbench().getProgressService(),
? ? new IRunnableWithProgress() {
? ? ? public void run(IProgressMonitor monitor) {
? ? ? ? //do UI work
? ? ? }
? ? },
? ? Platform.getWorkspace().getRoot());
效果:

這里最后一個參數(shù)可以是null,或者是這個操作的規(guī)則,在這里我們是設(shè)定運行這個UI操作時鎖定工作臺.
更加具體的可以參見:
http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/workbench_jobs.htm
另外,有少數(shù)時候,我們不想彈出一個進度條窗口,而是只在最底下的狀態(tài)欄顯示就可以了,很簡單,寫自己的Job類時,在構(gòu)造方法里加上一句:
setUser(false);就可以了.