6. Swing應(yīng)用中的多線程問題
Swing應(yīng)用運(yùn)行于多個(gè)線程之上挺举。特別是三類線程:
1.起始線程杀赢,或主線程,在main()方法中運(yùn)行湘纵,啟動(dòng)GUI的構(gòu)建和應(yīng)用的退出脂崔;
2.EDT線程;
3.一些處理計(jì)算密集型任務(wù)及IO的后臺(tái)線程梧喷。
所有的事件處理砌左,繪制和界面刷新代碼在單一線程(EDT)中進(jìn)行,這是為了確保事件處理器在下個(gè)處理器啟動(dòng)之前完成執(zhí)行铺敌,且繪制不被事件打斷汇歹。如果因計(jì)算密集型任務(wù)造成EDT饑餓,用戶界面將會(huì)掛起偿凭,程序?qū)τ脩艚换o響應(yīng)产弹。"理想情況下,任何需要花費(fèi)30-100 ms任務(wù)不應(yīng)該在EDT上運(yùn)行笔喉。否則用戶將感覺到輸入和UI應(yīng)答之間的停頓取视。
再者硝皂,所有訪問GUI組件的代碼應(yīng)該在EDT上運(yùn)行,因?yàn)檫@些組件中很多不能確保線程安全作谭,而從相同線程中訪問他們能避免多線程問題稽物。
總之,
1.耗時(shí)和阻塞IO任務(wù)不應(yīng)在EDT上運(yùn)行折欠,以避免造成EDT饑餓贝或,進(jìn)而影響用戶交互的事件觸發(fā)及界面刷新;
2.為了線程安全锐秦,Swing組件應(yīng)僅在EDT上訪問咪奖。
6.1 javax.Swing.SwingUtilities.invokeLater() 和invokeAndWait()
方法invokeLater(Runnable)和 invokeAndWait(Runnable)協(xié)調(diào)EDT中的Runnable任務(wù)。
為避免主線程(運(yùn)行main()方法)和EDT之間線程問題酱床,推薦使用javax.swing.SwingUtilities.invokeLater(Runnable)在EDT上創(chuàng)建GUI組件羊赵,如:
/** The entry main() method */
public static void main(String args[]) {
// Run the GUI codes on the event-dispatching thread for thread-safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("My Swing Application");
frame.setContentPane(new MyMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true); // show it
}
});
}
靜態(tài)方法SwingUtilities.invokelater()以Runnable對(duì)象(作為匿名內(nèi)部類執(zhí)行)為參數(shù),并在EDT上制定任務(wù)(具體在run()方法中)扇谣∶两荩可以從任何線程中調(diào)用invokeLater()來要求EDT執(zhí)行特定代碼,這些代碼可以寫在Runnable參數(shù)的run()方法中罐寨。invokeLater()會(huì)立即返回靡挥,而不等待EDT執(zhí)行這些代碼。
如果有需要可以使用invokeAndWait(), 這方法會(huì)等待EDT執(zhí)行指定代碼才返回鸯绿。對(duì)于applet跋破,推薦(在init())通過invokeAndWait()運(yùn)行GUI創(chuàng)建代碼。這是為了避免init()在GUI創(chuàng)建完成之前退出造成問題瓶蝴。如:
public class SwingTemplateJApplet extends JApplet {
/** init() to setup the GUI components */
@Override
public void init() {
// Run GUI codes in the Event-Dispatching thread for thread safety
try {
// Use invokeAndWait() to ensure that init() exits after GUI construction
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
// Set the content-pane of JApplet to an instance of main JPanel
setContentPane(new SwingTemplateJPanel());
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
也可用java.awt.EventQueue.invokeLater()代替javax.swing.SwingUtilities.invokeLater()毒返,javax.swing.SwingUtilities.invokeLater()是 java.awt.EventQueue.invokeLater()的進(jìn)一步封裝。
通過之前例子的追蹤可以發(fā)現(xiàn)如果沒有使用SwingUtilities.invokeLater()舷手,EDT在setVisible()方法之后啟動(dòng)饿悬。另一方面,invokerLater()啟動(dòng)EDT聚霜。
6.2 javax.swing.Timer (JDK 1.2)
如果需要在一定時(shí)間后或一段頻繁時(shí)間間隔內(nèi)更新組件,使用時(shí)間類如 javax.swing.Timer(JDK 1.2)或java.util.Timer(JDK 1. 3)珠叔。
對(duì)于javax.swing.Timer蝎宇,閱讀"animation using javax.swing.Timer"。
6.3 javax.swing.SwingWorker<T,V> (JDK 1.6)
如之前提到過的祷安,在Swing應(yīng)用中:
- 計(jì)算密集型任務(wù)不應(yīng)在EDT上運(yùn)行姥芥,以避免EDT在處理事件和重繪中出現(xiàn)饑餓;
- 為了線程安全汇鞭,Swing組件應(yīng)僅在EDT上訪問凉唐。
類javax.swing.SwingWorkder<T,V> 幫助管理唯一的EDT和幾個(gè)后臺(tái)工作者線程之間的交互庸追。它可以用來協(xié)調(diào)后臺(tái)線程中的計(jì)算密集型任務(wù)并返回EDT中產(chǎn)生的最終結(jié)果或中間結(jié)果。
定義 SwingWorker 類如:public abstract class SwingWorker<T,V> implements RunnableFuture
SwingWorker<T,V>是帶兩種類型參數(shù)的抽象類:T 指方法doInBackground()和get()最終結(jié)果類型台囱,而V指 方法publish()和process()中間結(jié)果類型淡溯。
RunnableFuture 接口是兩個(gè)接口的結(jié)合:Runnable 和Future。接口Runnable 聲明了抽象方法run()簿训;而 Future 聲明 get(), cancel(), isDone(), 和isCancelled()咱娶。
制定后臺(tái)任務(wù)
protected abstract T doInBackground() throws Exception
// Do this task in a background thread
protected void done()
// Executes on the Event-Dispatching thread after the doInBackground() method finishes.
public final T get() throws InterruptedException, ExecutionException
// Waits for doInBackground() to complete and gets the result.
// Calling get() on the Event-Dispatching thread blocks all events, including repaints,
// until the SwingWorker completes.
public final void execute()
// Schedules this SwingWorker for execution on one of the worker thread.
public final boolean cancel(boolean mayInterruptIfRunning)
// Attempts to cancel execution of this task.
public final boolean isDone()
// Returns true if this task has completed (normally or exception)
public final boolean isCancelled()
// Returns true if this task was cancelled before it completed normally
為在工作者線程中制定一項(xiàng)任務(wù),擴(kuò)展 SwingWorker<T,V>子類并重寫:
- doInBackground()指定執(zhí)行任務(wù)强品,在一個(gè)工作者線程中制定并返回類型為T的結(jié)果膘侮;
- done()方法在 doInBackground()完成后在EDT中運(yùn)行,用get()方法取得doInBackground()結(jié)果 (類型 T)的榛。
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
/** Test SwingWorker on the counter application with a compute-intensive task */
@SuppressWarnings("serial")
public class SwingWorkerCounter extends JPanel {
// For counter
private JTextField tfCount;
private int count = 0;
// For SwingWorker
JButton btnStartWorker; // to start the worker
private JLabel lblWorker; // for displaying the result
/** Constructor to setup the GUI components */
public SwingWorkerCounter () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
/** Create a SwingWorker instance to run a compute-intensive task
Final result is String, no intermediate result (Void) */
final SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
/** Schedule a compute-intensive task in a background thread */
@Override
protected String doInBackground() throws Exception {
// Sum from 1 to a large n
long sum = 0;
for (int number = 1; number < 1000000000; ++number) {
sum += number;
}
return sum + "";
}
/** Run in event-dispatching thread after doInBackground() completes */
@Override
protected void done() {
try {
// Use get() to get the result of doInBackground()
String result = get();
// Display the result in the label (run in EDT)
lblWorker.setText("Result is " + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
btnStartWorker = new JButton("Start Worker");
add(btnStartWorker);
btnStartWorker.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
worker.execute(); // start the worker thread
lblWorker.setText(" Running...");
btnStartWorker.setEnabled(false); // Each instance of SwingWorker run once
}
});
lblWorker = new JLabel(" Not started...");
add(lblWorker);
}
/** The entry main() method */
public static void main(String[] args) {
// Run the GUI construction in the Event-Dispatching thread for thread-safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("SwingWorker Test");
frame.setContentPane(new SwingWorkerCounter());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setVisible(true);
}
});
}
}
SwingWorker的實(shí)例設(shè)計(jì)只能運(yùn)行一次且不能重新啟動(dòng)琼了,需要?jiǎng)?chuàng)建新實(shí)例重新執(zhí)行任務(wù)。
發(fā)布并處理中間結(jié)果
不一定在done()中處理最終結(jié)果, 如有需要可以publish()并 process()中間結(jié)果夫晌。
@SafeVarargs
protected final void publish(V... chunks)
// Sends data chunks to the process(java.util.List<V>) method.
// This method shall be called inside the doInBackground() to deliver intermediate results
// for processing on the Event-Dispatching thread inside the process() method.
protected void process(List<V> chunks)
// Receives data chunks from publish() asynchronously on the Event-Dispatching thread.
在doInBackground()中, 用publish(V...) 發(fā)布一個(gè)或更多類型V中間結(jié)果雕薪。重寫process(List<V>)方法來處理List<V>中公布的結(jié)果,process()方法運(yùn)行在EDT中慷丽。
import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
/** Test SwingWorker on the counter application with a compute-intensive task */
@SuppressWarnings("serial")
public class SwingWorkerCounterIntermediateResult extends JPanel {
// For counter
private JTextField tfCount;
private int count = 0;
// For SwingWorker
JButton btnStartWorker; // to start the worker
private JLabel lblWorker; // for displaying the result
/** Constructor to setup the GUI components */
public SwingWorkerCounterIntermediateResult () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
/** Create a SwingWorker instance to run a compute-intensive task */
final SwingWorker<String, String> worker = new SwingWorker<String, String>() {
/** Schedule a compute-intensive task in a background thread */
@Override
protected String doInBackground() throws Exception {
long sum = 0;
for (int number = 0; number < 10000000; ++number) {
sum += number;
publish(sum + ""); // Send "every" intermediate result to process()
// You might not publish every intermediate result
}
return sum + "";
}
/** Run in event-dispatching thread after doInBackground() completes */
@Override
protected void done() {
try {
// Use get() to get the result of doInBackground()
String finalResult = get();
// Display the result in the label (run in EDT)
lblWorker.setText("Final Result is " + finalResult);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
/** Run in event-dispatching thread to process intermediate results
send from publish(). */
@Override
protected void process(java.util.List<String> chunks) {
// Get the latest result from the list
String latestResult = chunks.get(chunks.size() - 1);
lblWorker.setText("Result is " + latestResult);
}
};
btnStartWorker = new JButton("Start Worker");
add(btnStartWorker);
btnStartWorker.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
worker.execute(); // start the worker thread
lblWorker.setText(" Running...");
btnStartWorker.setEnabled(false); // SwingWorker can only run once
}
});
lblWorker = new JLabel(" Not started...");
add(lblWorker);
}
/** The entry main() method */
public static void main(String[] args) {
// Run the GUI construction in the Event-Dispatching thread for thread-safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("SwingWorker Test");
frame.setContentPane(new SwingWorkerCounterIntermediateResult());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setVisible(true);
}
});
}
}
屬性變化事件
doInBackground()發(fā)送PropertyChangeEvent給其所有的PropertyChangeListeners 關(guān)于邊界屬性變化蹦哼。有兩個(gè)邊界屬性:"state" 和"progress"。"state"在封裝枚舉類型SwingWorker.StateValue中定義要糊,StateValue取值為PENDING (SwingWorker 實(shí)例創(chuàng)建), START (doInBackground啟動(dòng))和DONE(doInBackground 完成) 纲熏。"progress" 是一個(gè)整數(shù)型,取值范圍從0到100锄俄【志ⅲ可以在doInBackground()中 通過setProgress()方法改變progress值來發(fā)送PropertyChangeEvent給其所有的PropertyChangeListeners。
例子
在本例 doInBackground()方法內(nèi)奶赠,調(diào)用setProgess()來改變progress邊界特征值(0-100之間),然后發(fā)送PropertyChangeEvent鱼填。用SwingWorker定義并注冊(cè) PropertyChangeListener , 在進(jìn)度條顯示progress值。事件處理器在EDT中運(yùn)行毅戈。
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
/** Test SwingWorker on the counter application with a compute-intensive task */
@SuppressWarnings("serial")
public class SwingWorkerCounterProgress extends JPanel {
// For counter
private JTextField tfCount;
private int count = 0;
// For SwingWorker
JButton btnStartWorker; // to start the worker
private JLabel lblWorker; // for displaying the result
JProgressBar pbWorker; // progress bar for the worker task
/** Constructor to setup the GUI components */
public SwingWorkerCounterProgress () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
/** Create a SwingWorker instance to run a compute-intensive task */
final SwingWorker<String, String> worker = new SwingWorker<String, String>() {
/** Schedule a compute-intensive task in a background thread */
@Override
protected String doInBackground() throws Exception {
long sum = 0;
int maxNumber = 10000000;
for (int number = 0; number < maxNumber; ++number) {
sum += number;
publish(sum + ""); // send intermediate result to process()
// Fire PropertyChangeEvent for the bound-property "progress"
setProgress(100 * (number + 1) / maxNumber);
}
return sum + "";
}
/** Run in event-dispatching thread after doInBackground() completes */
@Override
protected void done() {
try {
// Use get() to get the result of doInBackground()
String finalResult = get();
// Display the result in the label (run in EDT)
lblWorker.setText("Final Result is " + finalResult);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
/** Run in event-dispatching thread to process intermediate results
send from publish(). */
@Override
protected void process(java.util.List<String> chunks) {
// Get the latest result from the list
String latestResult = chunks.get(chunks.size() - 1);
lblWorker.setText("Result is " + latestResult);
}
};
/** Event handler for the PropertyChangeEvent of property "progress" */
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("progress")) { // check the property name
pbWorker.setValue((Integer)evt.getNewValue()); // update progress bar
}
}
});
btnStartWorker = new JButton("Start Worker");
add(btnStartWorker);
btnStartWorker.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
worker.execute(); // start the worker thread
lblWorker.setText(" Running...");
btnStartWorker.setEnabled(false); // SwingWorker can only run once
}
});
lblWorker = new JLabel(" Not started...");
add(lblWorker);
pbWorker = new JProgressBar();
add(pbWorker);
}
/** The entry main() method */
public static void main(String[] args) {
// Run the GUI construction in the Event-Dispatching thread for thread-safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("SwingWorker Test");
frame.setContentPane(new SwingWorkerCounterProgress());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setVisible(true);
}
});
}
}
6.4 總結(jié)
線程對(duì)構(gòu)建響應(yīng)性GUI很有必要苹丸,有一些應(yīng)該使用新線程的典型情形:
- 在主線程中創(chuàng)建一新線程來處理耗時(shí)初始化任務(wù)(如:磁盤I/O)以便GUI加載更快;
- 在EDT中創(chuàng)建一新線程來處理耗時(shí)任務(wù)(在事件處理器中)以便GUI保持響應(yīng)苇经;
- 用計(jì)時(shí)器處理重復(fù)任務(wù)赘理,在一定時(shí)間間隔或延時(shí)一段時(shí)間執(zhí)行;
- 如果操作需要等待其他線程或程序的信息扇单,創(chuàng)建一個(gè)新線程商模。
此外,計(jì)算密集型線程必須協(xié)作并將控制交與其他線程。
7. Thread Pool, Executor, Callable/Future (JDK 1.5)
線程池支持類在JDK1.5 java.lang.concurrent包中引入:
7.1 線程池(Thread Pool)
線程池是一個(gè)能執(zhí)行任務(wù)線程的管理集合施流。當(dāng)使用線程池執(zhí)行大量任務(wù)時(shí)响疚,效率會(huì)因線程循環(huán)執(zhí)行任務(wù)而提高,這會(huì)降低每個(gè)任務(wù)調(diào)用開銷瞪醋。
可以實(shí)現(xiàn)接口ExecutorService來使用線程池忿晕,如ThreadPoolExecutor 或ScheduledThreadPoolExecutor,而在Executors 類中提供了更多便捷工廠方法趟章,列舉如下:
-
Executors.newSingleThreadExecutor()
: 創(chuàng)建單一后臺(tái)線程杏糙; -
Executors.newFixedThreadPool(int numThreads)
: 創(chuàng)建固定大小的線程池; -
Executors.newCachedThreadPool()
: 創(chuàng)建帶自動(dòng)線程回收機(jī)制的無界線程池蚓土。
使用線程池的步驟:
- 寫一個(gè)實(shí)現(xiàn)Runnable接口的工作者線程類宏侍,run()方法制定運(yùn)行線程的行為;
- 用Executors 類提供的工廠方法創(chuàng)建線程池(ExecutorService)蜀漆,該線程池可以只有一個(gè)線程谅河,固定數(shù)量線程或無限數(shù)量線程;
- 創(chuàng)建工作者線程類實(shí)例确丢。用線程池的方法execute(Runnable r)來添加Runnable 任務(wù)到線程池绷耍,該任務(wù)將被協(xié)調(diào)并在有空閑線程時(shí)得到執(zhí)行。
7.2 接口java.util.concurrent.Executor
Executor對(duì)象能執(zhí)行提交的Runnable 任務(wù)鲜侥,該接口聲明了下面抽象方法:
public void execute(Runnable r)
在未來某個(gè)時(shí)候執(zhí)行給定任務(wù)褂始,任務(wù)因Executor執(zhí)行情況可能在新線程,線程池或當(dāng)前線程中執(zhí)行描函。
7.3 接口java.util.concurrent.ExecutorService
接口ExecutorService 聲明很多抽象方法崎苗,其中比較重要的有:
public void shutdown();
// Initiates an orderly shutdown of the thread pool.
// The previously executed/submitted tasks are allowed to complete,
// but no new tasks will be scheduled.
public <T> Future<T> submit(Callable<T> task);
// Submit or schedule the callable task for execution, which returns a Future object.
7.4 類java.util.concurrent.Executors
類Executors 提供創(chuàng)建Executor對(duì)象的工廠方法。如:
static ExecutorService newSingleThreadExecutor()
static ExecutorService newFixedThreadPool(int nThreads)
static ExecutorService newCachedThreadPool()
static ScheduledExecutorService newSingleThreadScheduledExecutor()
static ScheduledExecutorService newScheduledThreadPool(int size)
public class WorkerThread implements Runnable {
private int workerNumber;
WorkerThread(int workerNumber) {
this.workerNumber = workerNumber;
}
public void run() {
// The thread simply prints 1 to 5
for (int i = 1; i <= 5; ++i) {
System.out.printf("Worker %d: %d\n", workerNumber, i);
try {
// sleep for 0 to 0.5 second
Thread.sleep((int)(Math.random() * 500));
} catch (InterruptedException e) {}
}
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
int numWorkers = Integer.parseInt(args[0]);
int threadPoolSize = Integer.parseInt(args[1]);
ExecutorService pool =
Executors.newFixedThreadPool(threadPoolSize);
WorkerThread[] workers = new WorkerThread[numWorkers];
for (int i = 0; i < numWorkers; ++i) {
workers[i] = new WorkerThread(i+1);
pool.execute(workers[i]);
}
pool.shutdown();
}
}
//2 threads, 5 workers
> java ThreadPoolTest 5 2
Worker 1: 1
Worker 2: 1
Worker 1: 2
Worker 1: 3
Worker 2: 2
Worker 1: 4
Worker 2: 3
Worker 1: 5
Worker 3: 1
Worker 3: 2
Worker 2: 4
Worker 3: 3
Worker 2: 5
Worker 3: 4
Worker 3: 5
Worker 4: 1
Worker 4: 2
Worker 5: 1
Worker 4: 3
Worker 5: 2
Worker 5: 3
Worker 4: 4
Worker 5: 4
Worker 5: 5
Worker 4: 5
首先安排工作者1和2用線程池中的兩個(gè)線程執(zhí)行舀寓,接著是工作者3胆数,4和5。占用線程任務(wù)完成后互墓,線程返回線程池必尼,然后安排另外一個(gè)任務(wù)并開始執(zhí)行。
可以調(diào)用pool.shutdown()來關(guān)閉線程池中所有線程篡撵。
7.5 接口java.util.concurrent.Callable<V> 和Future<V>
Callable 類似于Runnable判莉,不同的是 Callable提供方法返回結(jié)果或Exception到相應(yīng)的線程中。Callable聲明了抽象方法call()(不同于Runnable中的run())育谬。
public V call() // Call() returns a result of type <V>, or throws an exception if unable to do so.
在線程池中骂租,使用submit(Callable r)而非execute(Runnable r),這會(huì)返回一個(gè)Future<V> 對(duì)象(在ExecutorService接口中聲明)斑司。當(dāng)需要結(jié)果時(shí),可以對(duì)Future對(duì)象調(diào)用get()方法加以獲取。結(jié)果一旦完成便會(huì)返回宿刮,否則當(dāng)前線程一直阻塞直到獲取結(jié)果互站。
接口Future<V> 聲明了下面的抽象方法:
V get() // wait if necessary, retrieve result
V get(long timeout, TimeUnit unit)
boolean cancel(boolean mayInterruptIfRunning)
boolean isCancelled()
boolean isDone() // return true if this task completed
import java.util.concurrent.Callable;
public class CallableWorkerThread implements Callable<String> {
private int workerNumber;
CallableWorkerThread(int workerNumber) {
this.workerNumber = workerNumber;
}
public String call() { // use call() instead of run()
for (int i = 1; i <= 5; ++i) { // just print 1 to 5
System.out.printf("Worker %d: %d\n", workerNumber, i);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
return "worker " + workerNumber;
}
}
import java.util.concurrent.*;
public class CallableThreadPoolTest {
public static void main(String[] args) {
int numWorkers = Integer.parseInt(args[0]);
ExecutorService pool = Executors.newCachedThreadPool();
CallableWorkerThread workers[] = new CallableWorkerThread[numWorkers];
Future[] futures = new Future[numWorkers];
for (int i = 0; i < numWorkers; ++i) {
workers[i] = new CallableWorkerThread(i + 1);
futures[i] = pool.submit(workers[i]);
}
for (int i = 0; i < numWorkers; ++i) {
try {
System.out.println(futures[i].get() + " ended");
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (ExecutionException ex) {
ex.printStackTrace();
}
}
}
}
三個(gè)工作者線程的輸出:
> java CallableThreadPoolTest 3
Worker 1: 1
Worker 3: 1
Worker 2: 1
Worker 3: 2
Worker 1: 2
Worker 2: 2
Worker 2: 3
Worker 2: 4
Worker 2: 5
Worker 1: 3
Worker 3: 3
Worker 3: 4
Worker 3: 5
Worker 1: 4
Worker 1: 5
worker 1 ended
worker 2 ended
worker 3 ended
7.6 JDK 1.5中其他新線程特性
- Lock
- ReadWriteLock
- Semaphores
- Atomics
- Blocking Queue
更多參考文獻(xiàn)及資源
- Swing Tutorial's "Concurrency in Swing".
- John O'Conner, "Improve Application Performance With SwingWorker in Java SE 6" @ http://java.sun.com/developer/technicalArticles/javase/swingworker/.
翻譯自:
https://www3.ntu.edu.sg/home/ehchua/programming/java/j5e_multithreading.html