實現ThreadFactory介面生成自定義執行緒
實現ThreadFactory介面生成自定義執行緒
在面向物件程式設計領域中,工廠模式是廣泛使用的設計模式,創造性的通過開發類來建立一個或多個類的物件。當想要建立其中一個類的物件時,我們使用工廠來代替新的操作符。
考慮到在建立具有有限資源的物件時所遇到的限制,在工廠模式中將對物件建立集中化,因此在更改建立的物件類或者物件方式上更有優勢。例如,只有一個型別的N個物件,就能夠輕鬆地生成關於物件建立的統計資料。
Java提供ThreadFactory介面來實現Thread物件工廠。Java併發API中一些高階功能,例如fork/join的Executor框架,就使用執行緒工廠建立執行緒。Java併發API中裡一個工廠模式的例子是Executors類,它提供了大量建立不同類別的Executor物件的方法。本節將通過繼承Thread類新增新功能,實現一個新的執行緒工廠類來生成執行緒。
準備工作
本範例通過Eclipse開發工具實現。如果使用諸如NetBeans的開發工具,開啟並建立一個新的Java專案。
實現過程
通過如下步驟實現範例:
-
建立名為MyThread的類,繼承Thread類:
public class MyThread extends Thread{
-
宣告名為creationDate、startDate和finishDate三個私有Date屬性:
private final Date creationDate; private Date startDate; private Date finishDate;
-
實現類建構函式,將name和待執行的Runnable物件作為引數接收。初始化執行緒的建立時間:
public MyThread(Runnable target, String name ){ super(target,name); creationDate = new Date(); }
-
實現run()方法,儲存執行緒的起始時間,呼叫父類的run()方法,儲存執行的結束時間:
@Override public void run() { setStartDate(); super.run(); setFinishDate(); }
-
實現建立startDate屬性值的方法:
public synchronized void setStartDate
-
實現建立finishDate屬性值的方法:
public synchronized void setFinishDate() { finishDate=new Date(); }
-
實現名為getExecutionTime()的方法,通過完成時間與開始時間的差值來計算執行緒的執行時間:
public synchronized long getExecutionTime() { return finishDate.getTime()-startDate.getTime(); }
-
重寫toString()方法,返回執行緒的建立時間和執行時間:
@Override public synchronized String toString(){ StringBuilder buffer=new StringBuilder(); buffer.append(getName()); buffer.append(": "); buffer.append(" Creation Date: "); buffer.append(creationDate); buffer.append(" : Running time: "); buffer.append(getExecutionTime()); buffer.append(" Milliseconds."); return buffer.toString(); } }
-
建立名為MyThreadFactory的類,實現ThreadFactory介面:
public class MyThreadFactory implements ThreadFactory{
-
宣告名為counter的私有AtomicInteger屬性:
private AtomicInteger counter;
-
宣告名為prefix的私有String屬性:
private String prefix;
-
實現類建構函式,初始化屬性:
public MyThreadFactory (String prefix) { this.prefix=prefix; counter=new AtomicInteger(1); }
-
實現newThread()方法,建立MyThread物件且遞增counter屬性:
@Override public Thread newThread(Runnable r) { MyThread myThread=new MyThread(r,prefix+"-"+counter.getAndIncrement()); return myThread; } }
-
建立名為MyTask的類來實現Runnable介面,實現run()方法,設定當前執行緒休眠2秒鐘:
public class MyTask implements Runnable{ @Override public void run() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
通過建立名為Main的類,新增main()方法,實現本範例主類:
public class Main { public static void main(String[] args) throws Exception {
-
建立MyThreadFactory物件:
MyThreadFactory myFactory=new MyThreadFactory("MyThreadFactory");
-
建立Task物件:
MyTask task=new MyTask();
-
使用工廠的newThread()方法建立MyThread物件,用來執行任務:
Thread thread=myFactory.newThread(task);
-
啟動執行緒,然後等待執行緒結束:
thread.start(); thread.join();
-
使用toString()方法輸出執行緒資訊到控制檯:
System.out.printf("Main: Thread information.\n"); System.out.printf("%s\n",thread); System.out.printf("Main: End of the example.\n"); } }
工作原理
本節通過繼承Thread類實現了定製化的Mythread類。此類包含三個屬性,分別儲存執行緒建立和執行的開始時間,以及執行緒執行的結束時間。使用開始和結束時間屬性,實現了getExecutionTime()方法,返回執行緒在執行任務時消耗的總時間。最後,重寫toString()方法生成執行緒相關資訊。
一旦執行緒類自定義後,就通過實現ThreadFactory介面生成一個工廠來建立類物件。如果要將工廠作為獨立物件,則不需要強制使用此介面,但是如果要將此工廠與Java併發API的其他類一起使用,則必須通過實現此介面來構建工廠。ThreadFactory介面只有一個方法:newThread()方法。此方法將Runnble物件作為引數接收,並返回Thread物件來執行Runnable物件。本範例中返回MyThread物件。
為了檢查這兩個類,實現MyTask類,在MyThread物件管理的執行緒中執行的任務 ,此類實現Runnable介面。一個MyTask例項設定其執行執行緒休眠2秒鐘。
在本範例主方法中,使用執行Task物件的MyThreadFactory工廠來建立MyThread物件。如果執行本範例,將會看到執行緒啟動時間和執行緒執行時間的資訊。
下圖顯示本範例在控制檯輸出的執行資訊:
擴充套件學習
Java併發API提供Executors類來生成執行緒執行器,通常是ThreadPoolExecutor類的物件。還可以使用此類獲得ThreadFactory介面的最基本實現,通過使用defaultThreadFactory()方法。此方法生成的工廠建立屬於同一個ThreadGroup物件的基本執行緒物件,ThreadFactory介面可以用於任何目的,不只是與Executor框架相關。