有人可以简要解释如何使用ThreadFactory吗?使用和不使用ThreadFactory的示例可能对理解差异非常有帮助。
谢谢!
答案 0 :(得分:57)
这是一种可能的用法。如果你有一个执行器服务,它以多线程的方式执行你的可运行任务,偶尔你的线程会因未捕获的异常而死。我们假设您不想记录所有这些异常。 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;
}
}
答案 1 :(得分:43)
工厂模式是一种创建设计模式,用于软件开发,以封装创建对象所涉及的过程。
假设我们有一些工作线程用于不同的任务,并希望它们具有特殊名称(比如用于调试目的)。所以我们可以实现一个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++);
}
}
如果您有这样的要求,那么在没有工厂或构建器模式的情况下实现它将非常困难。
ThreadFactory
是Java API的一部分,因为它也被其他类使用。所以上面的例子说明了为什么我们应该在某些场合使用'工厂来创建线程',但是,当然,完全没有必要实现java.util.concurrent.ThreadFactory
来完成这个任务。
答案 2 :(得分:18)
一些内部工作
除了一些不易看到的内部作品外,这个主题很清楚。 在使用构造函数创建线程时,新创建的线程继承当前线程:
ThreadGroup
(除非提供或System.getSecurityManager().getThreadGroup()
返回任意ThreadGroup
) - 在某些情况下,其右侧的线程组可能很重要,并且可能导致不正确的线程终止/中断。 ThreadGroup
将作为默认的异常处理程序。ContextClassLoader
- 在托管环境中不应该是一个很大的问题,因为环境应该切换CCL,但如果你要实现它 - 请记住。泄漏调用者的CCL非常糟糕,线程组也是如此(特别是如果threadGroup是某个子类而不是直接java.lang.ThreadGroup
- 需要覆盖ThreadGroup.uncaughtException
)AccessControlContext
- 这里几乎没有什么可做的(除了从专用线程开始),因为该字段仅供内部使用,甚至很少怀疑是否存在。InheritableThreadLocal
- 这可能(或可能不会)导致一些影响。除了将线程产生到专用线程之外,再没有什么可以做的。根据应用程序的不同,上述各点可能根本没有任何效果,但在某些情况下,其中一些可能导致难以检测的类/资源泄漏,并且表现出不确定的行为。
那会是一个超长的帖子但是......
下面是ThreadFactory
实现的一些(希望)可重用代码,它可以在托管环境中使用,以确保正确ThreadGroup
(可以限制优先级或中断线程) ,ContextClassLoader
,stacksize等设置(和/或可以配置)而不泄漏。如果有任何兴趣,我可以展示如何处理w / inherited ThreadLocals
或继承的acc(基本上可以泄漏调用classloader
)
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();
答案 3 :(得分:4)
恕我直言ThreadFactory
的一个最重要的功能是命名线程有用的东西。在诊断问题时,在名为pool-1-thread-2
或更差Thread-12
的堆栈跟踪中使用线程是一件非常痛苦的事。
当然,拥有ThreadGroup
,守护程序状态和优先级也都很有用。
答案 4 :(得分:2)
如“InsertNickHere”所述,您必须了解Factory Pattern。
使用ThreadFactory的一个很好的例子是ThreadPoolExecutor: 如有必要,Executor将创建线程并处理池。如果您想介入创建过程并为创建的Thread赋予特殊名称,或者将它们分配给ThreadGroup,您可以为此创建一个ThreadFactory并将其提供给Executor。
有点IoC - 风格。
答案 5 :(得分:2)
使用自定义线程工厂始终是一个好习惯。默认工厂使用不多。您应该使用自定义工厂,原因如下:
查看此帖子: http://wilddiary.com/understanding-java-threadfactory-creating-custom-thread-factories/
答案 6 :(得分:2)
Java中的ThreadFactory用法
按需创建新线程的对象。使用线程工厂删除了对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;
}
}
答案 7 :(得分:1)
ThreadFactory是一个具有单个方法的接口
public abstract java.lang.Thread newThread(java.lang.Runnable arg0);
其用途取决于您的要求。假设您希望特定功能始终创建守护程序线程。您可以使用ThreadFactory轻松实现此目的。
以下代码仅用于说明基础知识。它没有做任何特定的功能。
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());
}
}
}
答案 8 :(得分:0)
从VerboseThreads
查看jcabi-log(实施ThreadFactory
)。此实现使Thread
在异常被抛出时记录异常。当您需要查看线程何时以及为何死亡时,非常有用的类。
答案 9 :(得分:0)
ThreadFactory
将非常有用
您可以使用Google Guava lib中的ThreadFactoryBuilder
来创建ThreadFactory
,就像这样
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MyThreadPool-Worker-%d")
.setDaemon(true)
.build();