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