在面向对象编程领域中,工厂模式是广泛使用的设计模式,创造性的通过开发类来创建一个或多个类的对象。当想要创建其中一个类的对象时,我们使用工厂来代替新的操作符。
考虑到在创建具有有限资源的对象时所遇到的限制,在工厂模式中将对对象创建集中化,因此在更改创建的对象类或者对象方式上更有优势。例如,只有一个类型的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() { startDate=new Date(); }
-
实现建立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框架相关。