View Javadoc
1   /*
2    * Copyright (C) 2010 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.util.concurrent;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  
20  import com.google.common.annotations.GwtIncompatible;
21  import com.google.errorprone.annotations.CanIgnoreReturnValue;
22  import java.lang.Thread.UncaughtExceptionHandler;
23  import java.util.Locale;
24  import java.util.concurrent.Executors;
25  import java.util.concurrent.ThreadFactory;
26  import java.util.concurrent.atomic.AtomicLong;
27  import javax.annotation.CheckReturnValue;
28  
29  /**
30   * A ThreadFactory builder, providing any combination of these features:
31   * <ul>
32   * <li>whether threads should be marked as {@linkplain Thread#setDaemon daemon} threads
33   * <li>a {@linkplain ThreadFactoryBuilder#setNameFormat naming format}
34   * <li>a {@linkplain Thread#setPriority thread priority}
35   * <li>an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception handler}
36   * <li>a {@linkplain ThreadFactory#newThread backing thread factory}
37   * </ul>
38   * <p>If no backing thread factory is provided, a default backing thread factory is used as if by
39   * calling {@code setThreadFactory(}{@link Executors#defaultThreadFactory()}{@code )}.
40   *
41   * @author Kurt Alfred Kluever
42   * @since 4.0
43   */
44  @CanIgnoreReturnValue
45  @GwtIncompatible
46  public final class ThreadFactoryBuilder {
47    private String nameFormat = null;
48    private Boolean daemon = null;
49    private Integer priority = null;
50    private UncaughtExceptionHandler uncaughtExceptionHandler = null;
51    private ThreadFactory backingThreadFactory = null;
52  
53    /**
54     * Creates a new {@link ThreadFactory} builder.
55     */
56    public ThreadFactoryBuilder() {}
57  
58    /**
59     * Sets the naming format to use when naming threads ({@link Thread#setName}) which are created
60     * with this ThreadFactory.
61     *
62     * @param nameFormat a {@link String#format(String, Object...)}-compatible format String, to which
63     *     a unique integer (0, 1, etc.) will be supplied as the single parameter. This integer will
64     *     be unique to the built instance of the ThreadFactory and will be assigned sequentially. For
65     *     example, {@code "rpc-pool-%d"} will generate thread names like {@code "rpc-pool-0"},
66     *     {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc.
67     * @return this for the builder pattern
68     */
69    public ThreadFactoryBuilder setNameFormat(String nameFormat) {
70      String unused = format(nameFormat, 0); // fail fast if the format is bad or null
71      this.nameFormat = nameFormat;
72      return this;
73    }
74  
75    /**
76     * Sets daemon or not for new threads created with this ThreadFactory.
77     *
78     * @param daemon whether or not new Threads created with this ThreadFactory will be daemon threads
79     * @return this for the builder pattern
80     */
81    public ThreadFactoryBuilder setDaemon(boolean daemon) {
82      this.daemon = daemon;
83      return this;
84    }
85  
86    /**
87     * Sets the priority for new threads created with this ThreadFactory.
88     *
89     * @param priority the priority for new Threads created with this ThreadFactory
90     * @return this for the builder pattern
91     */
92    public ThreadFactoryBuilder setPriority(int priority) {
93      // Thread#setPriority() already checks for validity. These error messages
94      // are nicer though and will fail-fast.
95      checkArgument(
96          priority >= Thread.MIN_PRIORITY,
97          "Thread priority (%s) must be >= %s",
98          priority,
99          Thread.MIN_PRIORITY);
100     checkArgument(
101         priority <= Thread.MAX_PRIORITY,
102         "Thread priority (%s) must be <= %s",
103         priority,
104         Thread.MAX_PRIORITY);
105     this.priority = priority;
106     return this;
107   }
108 
109   /**
110    * Sets the {@link UncaughtExceptionHandler} for new threads created with this ThreadFactory.
111    *
112    * @param uncaughtExceptionHandler the uncaught exception handler for new Threads created with
113    *     this ThreadFactory
114    * @return this for the builder pattern
115    */
116   public ThreadFactoryBuilder setUncaughtExceptionHandler(
117       UncaughtExceptionHandler uncaughtExceptionHandler) {
118     this.uncaughtExceptionHandler = checkNotNull(uncaughtExceptionHandler);
119     return this;
120   }
121 
122   /**
123    * Sets the backing {@link ThreadFactory} for new threads created with this ThreadFactory. Threads
124    * will be created by invoking #newThread(Runnable) on this backing {@link ThreadFactory}.
125    *
126    * @param backingThreadFactory the backing {@link ThreadFactory} which will be delegated to during
127    *     thread creation.
128    * @return this for the builder pattern
129    *
130    * @see MoreExecutors
131    */
132   public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) {
133     this.backingThreadFactory = checkNotNull(backingThreadFactory);
134     return this;
135   }
136 
137   /**
138    * Returns a new thread factory using the options supplied during the building process. After
139    * building, it is still possible to change the options used to build the ThreadFactory and/or
140    * build again. State is not shared amongst built instances.
141    *
142    * @return the fully constructed {@link ThreadFactory}
143    */
144   @CheckReturnValue
145   public ThreadFactory build() {
146     return doBuild(this);
147   }
148 
149   // Split out so that the anonymous ThreadFactory can't contain a reference back to the builder.
150   // At least, I assume that's why. TODO(cpovirk): Check, and maybe add a test for this.
151   private static ThreadFactory doBuild(ThreadFactoryBuilder builder) {
152     final String nameFormat = builder.nameFormat;
153     final Boolean daemon = builder.daemon;
154     final Integer priority = builder.priority;
155     final UncaughtExceptionHandler uncaughtExceptionHandler = 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
163       public Thread newThread(Runnable runnable) {
164         Thread thread = backingThreadFactory.newThread(runnable);
165         if (nameFormat != null) {
166           thread.setName(format(nameFormat, count.getAndIncrement()));
167         }
168         if (daemon != null) {
169           thread.setDaemon(daemon);
170         }
171         if (priority != null) {
172           thread.setPriority(priority);
173         }
174         if (uncaughtExceptionHandler != null) {
175           thread.setUncaughtExceptionHandler(uncaughtExceptionHandler);
176         }
177         return thread;
178       }
179     };
180   }
181 
182   private static String format(String format, Object... args) {
183     return String.format(Locale.ROOT, format, args);
184   }
185 }