最近看了幾個多線程設計模式,對照J2SE5.0里的多線程工具,兩者很相似。從網上找了幾篇J2SE5.0多線程工具例子, 供參考
(轉自賽迪網)
Java自1995年面世以來得到了廣泛得一個運用,但是對多線程編程的支持Java很長時間一直停留在初級階段。在Java 5.0之前Java里的多線程編程主要是通過Thread類,Runnable接口,Object對象中的wait()、 notify()、 notifyAll()等方法和synchronized關鍵詞來實現的。這些工具雖然能在大多數情況下解決對共享資源的管理和線程間的調度,但存在以下幾個問題
1.?????
過于原始,拿來就能用的功能有限,即使是要實現簡單的多線程功能也需要編寫大量的代碼。這些工具就像匯編語言一樣難以學習和使用,比這更糟糕的是稍有不慎它們還可能被錯誤地使用,而且這樣的錯誤很難被發現。
2.?????
如果使用不當,會使程序的運行效率大大降低。
3.?????
為了提高開發效率,簡化編程,開發人員在做項目的時候往往需要寫一些共享的工具來實現一些普遍適用的功能。但因為沒有規范,相同的工具會被重復地開發,造成資源浪費。
4.?????
因為鎖定的功能是通過
Synchronized
來實現的,這是一種塊結構,只能對代碼中的一段代碼進行鎖定,而且鎖定是單一的。如以下代碼所示:
synchronized
(
lock
)
{
?
?? //
執行對共享資源的操作
??? ……
}
|
??? ?
一些復雜的功能就很難被實現。比如說如果程序需要取得
lock A
和
lock B
來進行操作
1
,然后需要取得
lock C
并且釋放
lock A
來進行操作
2
,
Java 5.0
之前的多線程框架就顯得無能為力了。
?? 因為這些問題,程序員對舊的框架一直頗有微詞。這種情況一直到
Java 5.0
才有較大的改觀,一系列的多線程工具包被納入了標準庫文件。這些工具包括了一個新的多線程程序的執行框架,使編程人員可方便地協調和調度線程的運行,并且新加入了一些高性能的常用的工具,使程序更容易編寫,運行效率更高。本文將分類并結合例子來介紹這些新加的多線程工具。
?? 在我們開始介紹
Java 5.0
里的新
Concurrent
工具前讓我們先來看一下一個用舊的多線程工具編寫的程序,這個程序里有一個
Server
線程,它需要啟動兩個
Component
,
Server
線程需等到
Component
線程完畢后再繼續。相同的功能在
Synchronizer
一章里用新加的工具
CountDownLatch
有相同的實現。兩個程序,孰優孰劣,哪個程序更容易編寫,哪個程序更容易理解,相信大家看過之后不難得出結論。
public class ServerThread {
????? Object concLock = new Object();
????? int count = 2;
public void runTwoThreads() {
????? //
啟動兩個線程去初始化組件
??????????? new Thread(new ComponentThread1(this)).start();
??????????? new Thread(new ComponentThread1(this)).start();
??????????? // Wait for other thread
while(count != 0) {
????????????????? synchronized(concLock) {
??????????????????????? try {
????????????????????????????? concLock.wait();
????????????????????????????? System.out.println("Wake up.");
??????????????????????? } catch (InterruptedException ie) { //
處理異常
}
????????????????? }
??????????? }
??????????? System.out.println("Server is up.");
????? }
????? public void callBack() {
synchronized(concLock) {
????????????????? count--;
????????????????? concLock.notifyAll();
??????????? }
????? }
????? public static void main(String[] args){
??????????? ServerThread server = new ServerThread();
??????????? server.runTwoThreads();
????? }
}
?
public class ComponentThread1 implements Runnable {
????? private ServerThread server;
????? public ComponentThread1(ServerThread server) {
??????????? this.server = server;
????? }
public void run() {
????? //
做組件初始化的工作
??????????? System.out.println("Do component initialization.");
??????????? server.callBack();
????? }
}
|
1:三個新加的多線程包
?? Java 5.0
里新加入了三個多線程包:
java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.
-
java.util.concurrent
包含了常用的多線程工具,是新的多線程工具的主體。
-
java.util.concurrent.atomic
包含了不用加鎖情況下就能改變值的原子變量,比如說
AtomicInteger
提供了
addAndGet()
方法。
Add
和
Get
是兩個不同的操作,為了保證別的線程不干擾,以往的做法是先鎖定共享的變量,然后在鎖定的范圍內進行兩步操作。但用
AtomicInteger.addAndGet()
就不用擔心鎖定的事了,其內部實現保證了這兩步操作是在原子量級發生的,不會被別的線程干擾。
-
java.util.concurrent.locks
包包含鎖定的工具。
2:Callable 和 Future接口
?? Callable
是類似于
Runnable
的接口,實現
Callable
接口的類和實現
Runnable
的類都是可被其它線程執行的任務。
Callable
和
Runnable
有幾點不同:
-
Callable
規定的方法是
call()
,而
Runnable
規定的方法是
run().
-
Callable
的任務執行后可返回值,而
Runnable
的任務是不能返回值的。
-
call
()方法可拋出異常,而
run
()方法是不能拋出異常的。
-
運行
Callable
任務可拿到一個
Future
對象,通過
Future
對象可了解任務執行情況,可取消任務的執行,還可獲取任務執行的結果。
以下是
Callable
的一個例子:
public class DoCallStuff implements Callable{ // *1
??????? private int aInt;
??????? public DoCallStuff(int aInt) {
??????????????? this.aInt = aInt;
??????? }
??????? public String call() throws Exception { //*2
??????????????? boolean resultOk = false;
??????????????? if(aInt == 0){
??????????????????????? resultOk = true;
??????????????? }? else if(aInt == 1){
??????????????????????? while(true){ //infinite loop
??????????????????
?????????????System.out.println("looping....");
??????????????????????????????? Thread.sleep(3000);
??????????????????????? }
??????????????? } else {
??????????????????????? throw new Exception("Callable terminated with Exception!"); //*3
??????????????? }
??????????????? if(resultOk){
??????????????????????? return "Task done.";
??????????????? } else {
??????????????????????? return "Task failed";
??????????????? }
??????? }
}
|
*1:
名為
DoCallStuff
類實現了
Callable
,
String
將是
call
方法的返回值類型。例子中用了
String
,但可以是任何
Java
類。
*2: call
方法的返回值類型為
String
,這是和類的定義相對應的。并且可以拋出異常。
*3: call
方法可以拋出異常,如加重的斜體字所示。
以下是調用
DoCallStuff
的主程序。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Executor {
??????? public static void main(String[] args){
??????????????? //*1
??????????????? DoCallStuff call1 = new DoCallStuff(0);
??????????????? DoCallStuff call2 = new DoCallStuff(1);
??????????????? DoCallStuff call3 = new DoCallStuff(2);
??????????????? //*2
??????????????? ExecutorService es = Executors.newFixedThreadPool(3);
??????????????? //*3
??????????????? Future future1 = es.submit(call1);
??????????????? Future future2 = es.submit(call2);
????
???????????Future future3 = es.submit(call3);
??????????????? try {
??????????????????????? //*4
??????????????????????? System.out.println(future1.get());
???????????????????????? //*5
??????????????????????? Thread.sleep(3000);
?????????????????
??????System.out.println("Thread 2 terminated? :" + future2.cancel(true));
??????????????????????? //*6
??????????????????????? System.out.println(future3.get());
??????????????? } catch (ExecutionException ex) {
??????????????????????? ex.printStackTrace();
??????????????? } catch (InterruptedException ex) {
??????????????????????? ex.printStackTrace();
??????????????? }
??????? }
}