Java中的ThreadFactory用法
有人可以简单地解释何时和何时使用ThreadFactory? 有和没有使用ThreadFactory的例子可能对理解差异很有帮助。
谢谢!
工厂模式是一种用于软件开发的创builddevise模式,用于封装创build对象所涉及的过程。
让我们假设我们有一些工作线程用于不同的任务,并希望他们有特殊的名字(比如出于debugging目的)。 所以我们可以实现一个ThreadFactory:
public class WorkerThreadFactory implements ThreadFactory { private int counter = 0; private String prefix = ""; public WorkerThreadFactory(String prefix) { this.prefix = prefix; } public Thread newThread(Runnable r) { return new Thread(r, prefix + "-" + counter++); } }
如果你有这样的要求,那么在没有工厂或build造者模式的情况下实现它就相当困难。
ThreadFactory
是Java API的一部分,因为它也被其他类使用。 所以上面的例子说明了为什么在某些场合我们应该使用“工厂创build线程”,当然,完全不需要实现java.util.concurrent.ThreadFactory
来完成这个任务。
这里有一个可能的用法。 如果你有一个执行器服务以multithreading的方式执行你的可运行任务,偶尔你的线程会从一个未捕获的exception中死亡。 假设你不想logging所有这些exception。 ThreadFactory
解决了这个问题:
ExecutorService executor = Executors.newSingleThreadExecutor(new LoggingThreadFactory()); executor.submit(new Runnable() { @Override public void run() { someObject.someMethodThatThrowsRuntimeException(); } });
LoggingThreadFactory
可以像这样实现:
public class LoggingThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { LoggerFactory.getLogger(t.getName()).error(e.getMessage(), e); } }); return t; } }
一些内部工作
除了一些不容易看到的内部作品之外,这个话题被覆盖得很好。 在创build一个线程w /构造函数时,新创build的线程inheritance当前线程:
- ThreadGroup(除非提供或System.getSecurityManager()。getThreadGroup()返回任意的ThreadGroup) – 在某些情况下,它自己的线程组可能很重要,并可能导致不正确的线程终止/中断。 ThreadGroup将作为默认的exception处理程序。
- ContextClassLoader – 在受pipe理的环境中,由于环境将切换CCL,所以不应该是一个很大的问题,但是如果要实现这一点 – 请记住。 泄漏调用者的CCL是相当不好的,线程组也是如此(尤其是如果threadGroup是某个子类而不是直接java.lang.ThreadGroup – 需要重写ThreadGroup.uncaughtException)
- AccessControlContext – 在这里,几乎没有什么可以做的(除了在专用线程开始),因为该字段仅供内部使用,很less甚至怀疑存在。
- 堆栈的大小(通常是没有指定的,但是它可以根据调用者得到一个非常窄的堆栈大小的线程)
- 优先 – 大多数人知道并倾向于设置它(或多或less)
- 守护进程的状态 – 通常这不是很重要,很容易知道(如果应用程序刚刚消失)
- 最后:线程inheritance调用者的InheritableThreadLocal – 这可能(或可能不)导致一些影响。 除了将线程产生到专用线程之外,再也没有什么可以做的了。
根据应用程序的不同,以上几点可能根本没有任何作用,但在某些情况下,其中一些可能会导致难以检测到的类/资源泄漏,并且performance出不确定的行为。
这将使一个特长的post,但如此…
下面是ThreadFactory实现的一些(希望)可重用的代码 ,它可以在托pipe环境中使用,以确保正确的ThreadGroup(可以限制优先级或中断线程),ContextClassLoader,stacksize等等被设置(和/或可以被configuration)没有泄漏。 如果有任何兴趣,我可以展示如何处理w /inheritance的ThreadLocals或inheritance的acc(这基本上可以泄漏调用类加载器)
package bestsss.util; import java.lang.Thread.UncaughtExceptionHandler; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; public class ThreadFactoryX implements ThreadFactory{ //thread properties long stackSize; String pattern; ClassLoader ccl; ThreadGroup group; int priority; UncaughtExceptionHandler exceptionHandler; boolean daemon; private boolean configured; private boolean wrapRunnable;//if acc is present wrap or keep it protected final AccessControlContext acc; //thread creation counter protected final AtomicLong counter = new AtomicLong(); public ThreadFactoryX(){ final Thread t = Thread.currentThread(); ClassLoader loader; AccessControlContext acc = null; try{ loader = t.getContextClassLoader(); if (System.getSecurityManager()!=null){ acc = AccessController.getContext();//keep current permissions acc.checkPermission(new RuntimePermission("setContextClassLoader")); } }catch(SecurityException _skip){ //no permission loader =null; acc = null; } this.ccl = loader; this.acc = acc; this.priority = t.getPriority(); this.daemon = true;//Executors have it false by default this.wrapRunnable = true;//by default wrap if acc is present (+SecurityManager) //default pattern - caller className StackTraceElement[] stack = new Exception().getStackTrace(); pattern(stack.length>1?getOuterClassName(stack[1].getClassName()):"ThreadFactoryX", true); } public ThreadFactory finishConfig(){ configured = true; counter.addAndGet(0);//write fence "w/o" volatile return this; } public long getCreatedThreadsCount(){ return counter.get(); } protected void assertConfigurable(){ if (configured) throw new IllegalStateException("already configured"); } private static String getOuterClassName(String className){ int idx = className.lastIndexOf('.')+1; className = className.substring(idx);//remove package idx = className.indexOf('$'); if (idx<=0){ return className;//handle classes starting w/ $ } return className.substring(0,idx);//assume inner class } @Override public Thread newThread(Runnable r) { configured = true; final Thread t = new Thread(group, wrapRunnable(r), composeName(r), stackSize); t.setPriority(priority); t.setDaemon(daemon); t.setUncaughtExceptionHandler(exceptionHandler);//securityException only if in the main group, shall be safe here //funny moment Thread.getUncaughtExceptionHandler() has a race.. badz (can throw NPE) applyCCL(t); return t; } private void applyCCL(final Thread t) { if (ccl!=null){//use factory creator ACC for setContextClassLoader AccessController.doPrivileged(new PrivilegedAction<Object>(){ @Override public Object run() { t.setContextClassLoader(ccl); return null; } }, acc); } } private Runnable wrapRunnable(final Runnable r){ if (acc==null || !wrapRunnable){ return r; } Runnable result = new Runnable(){ public void run(){ AccessController.doPrivileged(new PrivilegedAction<Object>(){ @Override public Object run() { r.run(); return null; } }, acc); } }; return result; } protected String composeName(Runnable r) { return String.format(pattern, counter.incrementAndGet(), System.currentTimeMillis()); } //standard setters allowing chaining, feel free to add normal setXXX public ThreadFactoryX pattern(String patten, boolean appendFormat){ assertConfigurable(); if (appendFormat){ patten+=": %d @ %tF %<tT";//counter + creation time } this.pattern = patten; return this; } public ThreadFactoryX daemon(boolean daemon){ assertConfigurable(); this.daemon = daemon; return this; } public ThreadFactoryX priority(int priority){ assertConfigurable(); if (priority<Thread.MIN_PRIORITY || priority>Thread.MAX_PRIORITY){//check before actual creation throw new IllegalArgumentException("priority: "+priority); } this.priority = priority; return this; } public ThreadFactoryX stackSize(long stackSize){ assertConfigurable(); this.stackSize = stackSize; return this; } public ThreadFactoryX threadGroup(ThreadGroup group){ assertConfigurable(); this.group= group; return this; } public ThreadFactoryX exceptionHandler(UncaughtExceptionHandler exceptionHandler){ assertConfigurable(); this.exceptionHandler= exceptionHandler; return this; } public ThreadFactoryX wrapRunnable(boolean wrapRunnable){ assertConfigurable(); this.wrapRunnable= wrapRunnable; return this; } public ThreadFactoryX ccl(ClassLoader ccl){ assertConfigurable(); this.ccl = ccl; return this; } }
还有一些非常简单的用法:
ThreadFactory factory = new TreadFactoryX().priority(3).stackSize(0).wrapRunnable(false).pattern("Socket workers", true). daemon(false).finishConfig();
恕我直言,一个ThreadFactory
最重要的function是命名线程有用的东西。 在名为pool-1-thread-2
或更糟的Thread-12
的堆栈跟踪中有线程是诊断问题时的一个完整的痛苦。
当然,有一个ThreadGroup
,守护进程的状态和优先级也是有用的。
正如“InsertNickHere”所提到的,您必须了解工厂模式 。
使用ThreadFactory的一个很好的例子是ThreadPoolExecutor :Executor将在必要时创buildThreads并负责合并。 如果您想要在创build过程中插入并为创build的线程指定特殊名称,或将其分配给一个线程组,则可以为此创build一个线程工厂,并将其提供给执行程序。
这有点IoC风格。
使用自定义线程工厂总是一个好习惯。 默认的工厂没有太多的用处。 出于以下原因,您应该使用自定义工厂:
- 要有自定义的线程名称
- 在线程types之间进行select
- select线程优先级
- 处理未捕获的exception
检查这个职位: http : //wilddiary.com/understanding-java-threadfactory-creating-custom-thread-factories/
ThreadFactory是一个单一方法的接口public abstract java.lang.Thread newThread(java.lang.Runnable arg0);
它的使用取决于您的要求。 假设你想要一个特定的function来总是创build守护进程线程。 你可以用ThreadFactory轻松实现。
下面的代码只是为了说明根本。 它没有做任何特定的function。
package TestClasses; import java.util.concurrent.ThreadFactory; public class ThreadFactoryEx implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } } package TestClasses; import java.util.concurrent.ThreadPoolExecutor; public class RunnableEx implements Runnable{ @Override public void run() { // TODO Auto-generated method stub for (int i = 0; i < 5; i++) { System.out.println("in a loop" + i + "times"); } } } package TestClasses; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Thread1 { public static void main(String[] args) { ExecutorService exe = Executors.newCachedThreadPool(new ThreadFactoryEx()); for (int i = 0; i < 4; i++) { exe.execute(new RunnableEx()); } } }
从jcabi-log看一下VerboseThreads
(实现ThreadFactory
)。 这个实现使得Thread
在被抛出exception时loggingexception。 非常有用的类,当你需要看到什么时候和为什么你的线程死亡。
Java中的ThreadFactory用法
按需创build新线程的对象。 使用线程工厂可以删除对new Thread
硬连线,使应用程序可以使用特殊的线程子类,优先级等。
这个接口最简单的实现就是:
class SimpleThreadFactory implements ThreadFactory { public Thread newThread(Runnable r) { return new Thread(r); } }
ThreadPoolExecutor.java的DefaultThreadFactory
static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
资源
ThreadFactory
将是有用的
- 用于设置更多的描述性线程名称
- 设置线程守护进程状态
- 用于设置线程优先级
你可以使用谷歌Guava lib中的ThreadFactory
来创build这样的ThreadFactory
ThreadFactory threadFactory = new ThreadFactoryBuilder() .setNameFormat("MyThreadPool-Worker-%d") .setDaemon(true) .build();