Может кто-нибудь кратко объяснить, КАК И КОГДА использовать ThreadFactory? Пример с использованием и без использования ThreadFactory может быть действительно полезен для понимания различий.
Спасибо!
Может кто-нибудь кратко объяснить, КАК И КОГДА использовать ThreadFactory? Пример с использованием и без использования ThreadFactory может быть действительно полезен для понимания различий.
Спасибо!
Шаблон factory представляет собой шаблон создания, используемый в разработке программного обеспечения для инкапсуляции процессов, связанных с созданием объектов.
Предположим, что у нас есть некоторые рабочие потоки для разных задач и они нуждаются в специальных именах (например, для целей отладки). Таким образом, мы могли бы реализовать 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++);
}
}
Если бы у вас было такое требование, было бы довольно сложно реализовать его без шаблона factory или построителя.
ThreadFactory
является частью Java API, потому что он также используется другими классами. Таким образом, приведенный выше пример показывает, почему мы должны использовать 'a factory для создания потоков в некоторых случаях, но, конечно, нет абсолютно никакой необходимости реализовывать java.util.concurrent.ThreadFactory
для выполнения этой задачи.
Здесь одно возможное использование. Если у вас есть служба-исполнитель, которая выполняет ваши запущенные задачи многопоточным способом, и время от времени ваш поток умирает от неперехваченного исключения. Предположим, что вы не будете регистрировать все эти исключения. 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;
}
}
Некоторые внутренние операции
Тема довольно хорошо освещена, за исключением некоторых внутренних работ, которые не так легко увидеть. При создании потока w/конструктор вновь созданный поток наследует текущие потоки:
В зависимости от приложения указанные выше точки могут вообще не иметь эффекта, но в некоторых случаях некоторые из них могут привести к утечкам класса/ресурсов, которые трудно обнаружить и проявить не детерминированное поведение.
Это сделает дополнительный длинный пост, но так...
Ниже приведен некоторый (надеюсь) многоразовый код для реализации ThreadFactory, он может использоваться в управляемых средах для обеспечения правильной ThreadGroup (которая может ограничивать потоки приоритета или прерывания), ContextClassLoader, стекизировать и т.д. set (и/или может быть настроен) и не просочиться. Если есть какой-либо интерес, я могу показать, как обращаться с унаследованными ThreadLocals или унаследованным 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();
IMHO единственная самая важная функция ThreadFactory
- это именовать потоки что-то полезное. Наличие потоков в stacktrace с именем pool-1-thread-2
или хуже Thread-12
является полной болью при диагностике проблем.
Конечно, наличие ThreadGroup
, статус и приоритет демона также полезны.
Как упоминалось в "InsertNickHere", вам нужно будет понять Factory Pattern.
Хорошим примером использования ThreadFactory является ThreadPoolExecutor: Исполнитель будет создавать темы, если это необходимо, и позаботиться об объединении. Если вы хотите войти в процесс создания и предоставить специальные имена созданным потокам или назначить их в ThreadGroup, вы можете создать ThreadFactory для этой цели и передать его Исполнителю.
Немного IoC-стиль.
Всегда полезно использовать пользовательский поток factory. Заводы по умолчанию не очень полезны. Вы должны использовать пользовательский factory по следующим причинам:
Отметьте это сообщение: http://wilddiary.com/understanding-java-threadfactory-creating-custom-thread-factories/
ThreadFactory - это интерфейс с одним методом
public abstract java.lang.Thread newThread(java.lang.Runnable arg0);
Его использование зависит от вашего требования. Предположим, вы хотите, чтобы определенная функциональность всегда создавала потоки Daemon. Вы можете легко достичь этого с помощью 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());
}
}
}
Использование ThreadFactory в Java
Объект, который создает новые потоки по требованию. Использование фабрик потоков удаляет жесткие вызовы на new Thread
, позволяя приложениям использовать специальные подклассы потоков, приоритеты и т.д.
Простейшая реализация этого интерфейса:
class SimpleThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
DefaultThreadFactory ThreadPoolExecutor.java
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;
}
}
Посмотрите VerboseThreads
(реализует ThreadFactory
) из jcabi-log. Эта реализация делает Thread
, что журналы исключений, когда они выбрасываются из них. Очень полезный класс, когда вам нужно посмотреть, когда и почему ваши нити умирают.
ThreadFactory
будет полезно
Вы можете использовать ThreadFactoryBuilder
из google Guava lib для создания ThreadFactory
, как этот
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("MyThreadPool-Worker-%d")
.setDaemon(true)
.build();