001/* 002 * Copyright (C) 2010 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.util.concurrent; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021 022import java.lang.Thread.UncaughtExceptionHandler; 023import java.util.Locale; 024import java.util.concurrent.Executors; 025import java.util.concurrent.ThreadFactory; 026import java.util.concurrent.atomic.AtomicLong; 027 028/** 029 * A ThreadFactory builder, providing any combination of these features: 030 * <ul> 031 * <li> whether threads should be marked as {@linkplain Thread#setDaemon daemon} 032 * threads 033 * <li> a {@linkplain ThreadFactoryBuilder#setNameFormat naming format} 034 * <li> a {@linkplain Thread#setPriority thread priority} 035 * <li> an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception 036 * handler} 037 * <li> a {@linkplain ThreadFactory#newThread backing thread factory} 038 * </ul> 039 * <p>If no backing thread factory is provided, a default backing thread factory is 040 * used as if by calling {@code setThreadFactory(}{@link 041 * Executors#defaultThreadFactory()}{@code )}. 042 * 043 * @author Kurt Alfred Kluever 044 * @since 4.0 045 */ 046public final class ThreadFactoryBuilder { 047 private String nameFormat = null; 048 private Boolean daemon = null; 049 private Integer priority = null; 050 private UncaughtExceptionHandler uncaughtExceptionHandler = null; 051 private ThreadFactory backingThreadFactory = null; 052 053 /** 054 * Creates a new {@link ThreadFactory} builder. 055 */ 056 public ThreadFactoryBuilder() {} 057 058 /** 059 * Sets the naming format to use when naming threads ({@link Thread#setName}) 060 * which are created with this ThreadFactory. 061 * 062 * @param nameFormat a {@link String#format(String, Object...)}-compatible 063 * format String, to which a unique integer (0, 1, etc.) will be supplied 064 * as the single parameter. This integer will be unique to the built 065 * instance of the ThreadFactory and will be assigned sequentially. For 066 * example, {@code "rpc-pool-%d"} will generate thread names like 067 * {@code "rpc-pool-0"}, {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. 068 * @return this for the builder pattern 069 */ 070 @SuppressWarnings("ReturnValueIgnored") 071 public ThreadFactoryBuilder setNameFormat(String nameFormat) { 072 format(nameFormat, 0); // fail fast if the format is bad or null 073 this.nameFormat = nameFormat; 074 return this; 075 } 076 077 /** 078 * Sets daemon or not for new threads created with this ThreadFactory. 079 * 080 * @param daemon whether or not new Threads created with this ThreadFactory 081 * will be daemon threads 082 * @return this for the builder pattern 083 */ 084 public ThreadFactoryBuilder setDaemon(boolean daemon) { 085 this.daemon = daemon; 086 return this; 087 } 088 089 /** 090 * Sets the priority for new threads created with this ThreadFactory. 091 * 092 * @param priority the priority for new Threads created with this 093 * ThreadFactory 094 * @return this for the builder pattern 095 */ 096 public ThreadFactoryBuilder setPriority(int priority) { 097 // Thread#setPriority() already checks for validity. These error messages 098 // are nicer though and will fail-fast. 099 checkArgument(priority >= Thread.MIN_PRIORITY, 100 "Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY); 101 checkArgument(priority <= Thread.MAX_PRIORITY, 102 "Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY); 103 this.priority = priority; 104 return this; 105 } 106 107 /** 108 * Sets the {@link UncaughtExceptionHandler} for new threads created with this 109 * ThreadFactory. 110 * 111 * @param uncaughtExceptionHandler the uncaught exception handler for new 112 * Threads created with this ThreadFactory 113 * @return this for the builder pattern 114 */ 115 public ThreadFactoryBuilder setUncaughtExceptionHandler( 116 UncaughtExceptionHandler uncaughtExceptionHandler) { 117 this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler); 118 return this; 119 } 120 121 /** 122 * Sets the backing {@link ThreadFactory} for new threads created with this 123 * ThreadFactory. Threads will be created by invoking #newThread(Runnable) on 124 * this backing {@link ThreadFactory}. 125 * 126 * @param backingThreadFactory the backing {@link ThreadFactory} which will 127 * be delegated to during thread creation. 128 * @return this for the builder pattern 129 * 130 * @see MoreExecutors 131 */ 132 public ThreadFactoryBuilder setThreadFactory( 133 ThreadFactory backingThreadFactory) { 134 this.backingThreadFactory = checkNotNull(backingThreadFactory); 135 return this; 136 } 137 138 /** 139 * Returns a new thread factory using the options supplied during the building 140 * process. After building, it is still possible to change the options used to 141 * build the ThreadFactory and/or build again. State is not shared amongst 142 * built instances. 143 * 144 * @return the fully constructed {@link ThreadFactory} 145 */ 146 public ThreadFactory build() { 147 return build(this); 148 } 149 150 private static ThreadFactory build(ThreadFactoryBuilder builder) { 151 final String nameFormat = builder.nameFormat; 152 final Boolean daemon = builder.daemon; 153 final Integer priority = builder.priority; 154 final UncaughtExceptionHandler uncaughtExceptionHandler = 155 builder.uncaughtExceptionHandler; 156 final ThreadFactory backingThreadFactory = 157 (builder.backingThreadFactory != null) 158 ? builder.backingThreadFactory 159 : Executors.defaultThreadFactory(); 160 final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; 161 return new ThreadFactory() { 162 @Override public Thread newThread(Runnable runnable) { 163 Thread thread = backingThreadFactory.newThread(runnable); 164 if (nameFormat != null) { 165 thread.setName(format(nameFormat, count.getAndIncrement())); 166 } 167 if (daemon != null) { 168 thread.setDaemon(daemon); 169 } 170 if (priority != null) { 171 thread.setPriority(priority); 172 } 173 if (uncaughtExceptionHandler != null) { 174 thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); 175 } 176 return thread; 177 } 178 }; 179 } 180 181 private static String format(String format, Object... args) { 182 return String.format(Locale.ROOT, format, args); 183 } 184}