Some internal operations
The topic is pretty well covered, with the exception of some internal works that are not so easy to see. When creating a w / stream, the constructor of the newly created stream inherits the current threads:
- ThreadGroup (if not specified, or System.getSecurityManager (). GetThreadGroup () returns an arbitrary ThreadGroup). In some cases, a thread group in its own right may be important and may lead to incorrect termination / termination of a thread. ThreadGroup will act as the default exception handler.
- ContextClassLoader - in a managed environment, which should not be a big problem, since the environment should switch CCL, but if you must implement this, do not forget. The leak of the calling CCL is pretty bad, as well as the thread group (especially if threadGroup is a subclass rather than a direct java.lang.ThreadGroup - you must override ThreadGroup.uncaughtException)
- AccessControlContext - there is practically nothing to do here (except for launching in a dedicated thread), since the field is intended for internal use only, and few even suspect the existence.
- stack size (usually not specified, but it can be used to get a stream with a very narrow stack size based on the caller)
- priority - most people know and tend to install it (more or less)
- daemon status - this is usually not very important and easily noticeable (if the application just disappears)
- Finally: the thread inherits the caller InheritableThreadLocal - which may (or may not) lead to some consequences. Again nothing can be done except that it spawns a stream into a dedicated stream.
Depending on the application, the above points may not have any effect at all, but in some cases some of them can lead to class / resource leaks that are difficult to detect and exhibit non-deterministic behavior.
This will make an extra long post, but so ...
Below is some (hopefully) reusable code for implementing ThreadFactory , it can be used in managed environments to ensure the correct ThreadGroup (which can limit priority or interrupt threads), ContextClassLoader, stack, etc. set (and / or can be customized) and do not leak. If there is any interest, I can show how to handle inherited ThreadLocals or inherited acc (which can essentially be missed by the calling 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; } }
Also very simple use:
ThreadFactory factory = new TreadFactoryX().priority(3).stackSize(0).wrapRunnable(false).pattern("Socket workers", true). daemon(false).finishConfig();
bestsss Jan 19 2018-11-11T00: 00Z
source share