代码之家  ›  专栏  ›  技术社区  ›  jagamot

Java中线程工厂的使用

  •  50
  • jagamot  · 技术社区  · 14 年前

    有人能简单地解释一下如何以及何时使用螺纹厂吗?使用和不使用threadfactory的示例对于理解这些差异可能非常有帮助。

    谢谢!

    10 回复  |  直到 7 年前
        1
  •  43
  •   buzz3791    11 年前

    工厂模式是一种创造性的设计模式,在软件开发中用于封装对象创建过程。

    假设我们有一些不同任务的工作线程,并希望它们具有特殊的名称(例如用于调试目的)。所以我们可以实现一个线程工厂:

    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
  •  57
  •   Boris Pavlović    14 年前

    这里有一个可能的用法。如果您有一个执行器服务,它以多线程的方式执行可运行的任务,并且您的线程偶尔会因未捕获的异常而死亡。假设您不记录所有这些异常。 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;
        }
    }
    
        3
  •  16
  •   ayushgp    7 年前

    一些内部加工

    除了一些不容易看得见的内部作品外,这个主题被很好地涵盖了。 创建带有构造函数的线程时,新创建的线程继承当前线程:

    • ThreadGroup (除非提供或 System.getSecurityManager().getThreadGroup() 返回任意值 使用线程组 )-在某些情况下,线程组本身可能很重要,并可能导致不正确的线程终止/中断。这个 使用线程组 将作为默认的异常处理程序。
    • ContextClassLoader -在管理环境中,这不应该是一个大问题,因为环境应该切换CCL,但如果要实现这一点,请记住。泄漏调用者的CCL是非常糟糕的,线程组也是如此(特别是如果线程组是一些子类而不是直接的话) java.lang.ThreadGroup -需要覆盖 ThreadGroup.uncaughtException )
    • AccessControlContext -在这里,几乎没有任何事情可以做(除了在专用线程中启动),因为该字段仅供内部使用,很少有人怀疑存在。
    • 堆栈大小(通常未指定,但根据调用方的不同,获取具有非常窄堆栈大小的线程是一件很有趣的事情)
    • 优先级-大多数人都知道并倾向于设置它(或多或少)
    • 守护进程状态-通常这不是很重要,也不容易被发现(如果应用程序刚刚消失)
    • 最后:线程继承调用方的 InheritableThreadLocal -这可能(或不可能)导致一些影响。同样,除了将线程生成到一个专用线程之外,什么都做不到。

    根据应用程序的不同,上面的点可能根本不起作用,但在某些情况下,其中一些点可能会导致类/资源泄漏,这些泄漏很难检测并显示出不确定性行为。


    那将是一个很长的职位,但是…

    下面是一些(希望)可重用的代码 ThreadFactory 实现,它可以在托管环境中使用,以确保 使用线程组 (可以限制优先级或中断线程) ContextClassLoader 设置(和/或可配置)和未泄漏的StackSize等。如果有兴趣,我可以告诉你如何处理继承 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();
    
        4
  •  4
  •   Jed Wesley-Smith    14 年前

    imho的一个最重要的功能 ThreadFactory 命名线程是否有用?在stacktrace中具有名为 pool-1-thread-2 或更糟 Thread-12 是诊断问题时的完全疼痛。

    当然,有一个 ThreadGroup ,守护进程状态和优先级也都很有用。

        5
  •  2
  •   Hardcoded    14 年前

    如“insertnickhere”所述,您必须理解 Factory Pattern .

    使用螺纹工厂的一个好例子是 ThreadPoolExecutor : 如果需要,执行器将创建线程并处理池。如果要在创建过程中介入并为创建的线程指定特殊名称,或者将它们分配给线程组,则可以为此创建一个线程工厂并将其交给执行器。

    有点像 IoC 风格。

        6
  •  2
  •   Drona    10 年前

    使用定制螺纹工厂是一个很好的实践。默认的工厂没有多大用处。您应该使用自定义工厂,原因如下:

    1. 具有自定义线程名称
    2. 在线程类型之间选择
    3. 选择线程优先级
    4. 处理未捕获的异常

    查看此日志: http://wilddiary.com/understanding-java-threadfactory-creating-custom-thread-factories/

        7
  •  2
  •   Premraj    9 年前

    Java中线程工厂的使用

    按需创建新线程的对象。使用线程工厂可以消除对 new Thread 使应用程序能够使用特殊的线程子类、优先级等。

    这个接口的最简单实现是:

    class SimpleThreadFactory implements ThreadFactory {
       public Thread newThread(Runnable r) {
         return new Thread(r);
       }
     }
    

    threadpoolexecutor.java的默认threadFactory

    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;
            }
        }
    

    Source

        8
  •  1
  •   Aalekh    10 年前

    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());
            }
        }
    }
    
        9
  •  0
  •   yegor256    12 年前

    看一看 VerboseThreads (器具) ThreadFactory jcabi-log . 此实现使 Thread 当异常被抛出时记录异常。非常有用的类,当您需要查看线程何时以及为什么会死。

        10
  •  0
  •   ravthiru    7 年前

    ThreadFactory 会有用的

    • 用于设置更具描述性的线程名称
    • 设置线程守护程序状态
    • 用于设置线程优先级

    你可以使用 ThreadFactoryBuilder 从google guava lib创建 线程工厂 这样地

    ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("MyThreadPool-Worker-%d")
            .setDaemon(true)
            .build();