Java編程之多線程&并發(fā)編程(下)

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)用中:

  1. 計(jì)算密集型任務(wù)不應(yīng)在EDT上運(yùn)行姥芥,以避免EDT在處理事件和重繪中出現(xiàn)饑餓;
  2. 為了線程安全汇鞭,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>子類并重寫:

  1. doInBackground()指定執(zhí)行任務(wù)强品,在一個(gè)工作者線程中制定并返回類型為T的結(jié)果膘侮;
  2. 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ī)制的無界線程池蚓土。

使用線程池的步驟:

  1. 寫一個(gè)實(shí)現(xiàn)Runnable接口的工作者線程類宏侍,run()方法制定運(yùn)行線程的行為;
  2. 用Executors 類提供的工廠方法創(chuàng)建線程池(ExecutorService)蜀漆,該線程池可以只有一個(gè)線程谅河,固定數(shù)量線程或無限數(shù)量線程;
  3. 創(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

Java參考及資源鏈接

更多參考文獻(xiàn)及資源

  1. Swing Tutorial's "Concurrency in Swing".
  2. 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

Java編程之多線程&并發(fā)編程(上)
Java編程之多線程&并發(fā)編程(中)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市僵缺,隨后出現(xiàn)的幾起案子胡桃,更是在濱河造成了極大的恐慌,老刑警劉巖磕潮,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翠胰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡自脯,警方通過查閱死者的電腦和手機(jī)之景,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膏潮,“玉大人锻狗,你說我怎么就攤上這事』啦危” “怎么了轻纪?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叠纷。 經(jīng)常有香客問我刻帚,道長(zhǎng),這世上最難降的妖魔是什么涩嚣? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任崇众,我火速辦了婚禮,結(jié)果婚禮上缓艳,老公的妹妹穿的比我還像新娘校摩。我一直安慰自己,他們只是感情好阶淘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布衙吩。 她就那樣靜靜地躺著,像睡著了一般溪窒。 火紅的嫁衣襯著肌膚如雪坤塞。 梳的紋絲不亂的頭發(fā)上时甚,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天骄呼,我揣著相機(jī)與錄音婉刀,去河邊找鬼废麻。 笑死茧妒,一個(gè)胖子當(dāng)著我的面吹牛捻脖,可吹牛的內(nèi)容都是我干的上鞠。 我是一名探鬼主播尺借,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盈电!你這毒婦竟也來了蝴簇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤匆帚,失蹤者是張志新(化名)和其女友劉穎熬词,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吸重,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡互拾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嚎幸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颜矿。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞭铆,靈堂內(nèi)的尸體忽然破棺而出或衡,到底是詐尸還是另有隱情,我是刑警寧澤车遂,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布封断,位于F島的核電站,受9級(jí)特大地震影響舶担,放射性物質(zhì)發(fā)生泄漏坡疼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一衣陶、第九天 我趴在偏房一處隱蔽的房頂上張望柄瑰。 院中可真熱鬧,春花似錦剪况、人聲如沸教沾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽授翻。三九已至,卻和暖如春孙咪,著一層夾襖步出監(jiān)牢的瞬間堪唐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工翎蹈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淮菠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓荤堪,卻偏偏與公主長(zhǎng)得像合陵,于是被迫代替她去往敵國和親枢赔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • layout: posttitle: 《Java并發(fā)編程的藝術(shù)》筆記categories: Javaexcerpt...
    xiaogmail閱讀 5,826評(píng)論 1 19
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法拥知,類相關(guān)的語法糠爬,內(nèi)部類的語法,繼承相關(guān)的語法举庶,異常的語法,線程的語...
    子非魚_t_閱讀 31,639評(píng)論 18 399
  • 1. 介紹-多任務(wù)和多線程 Java 支持單線程和多線程揩抡, 單線程程序有一個(gè)單一入口(main()函數(shù))及單一出...
    vancent閱讀 1,027評(píng)論 0 3
  • 今天孤身一人來到了海邊户侥,遠(yuǎn)望,海與天空的交線虛虛實(shí)實(shí)峦嗤。我踩在沙灘上蕊唐,沙子的觸感并沒有想象的柔軟細(xì)膩。海浪一朵朵拍來...
    綾綃閱讀 303評(píng)論 0 0
  • 美麗的女人都懂得愛惜自己,如鳥愛惜自己的羽毛装黑。而一個(gè)男人也應(yīng)該愛惜女人副瀑,就如愛惜自己的羽毛。 ——題記恋谭。 一糠睡、隱秘...
    景標(biāo)的楊閱讀 556評(píng)論 0 0