diff --git a/designer/src/com/fr/design/mainframe/bbs/UserInfoLabel.java b/designer/src/com/fr/design/mainframe/bbs/UserInfoLabel.java index 2af6adc62..c985fc6e5 100644 --- a/designer/src/com/fr/design/mainframe/bbs/UserInfoLabel.java +++ b/designer/src/com/fr/design/mainframe/bbs/UserInfoLabel.java @@ -16,6 +16,7 @@ import com.fr.design.gui.ilable.UILabel; import com.fr.design.gui.imenu.UIMenuItem; import com.fr.design.gui.imenu.UIPopupMenu; import com.fr.design.mainframe.DesignerContext; +import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.general.DateUtils; @@ -25,7 +26,6 @@ import com.fr.general.http.HttpClient; import com.fr.stable.EncodeConstants; import com.fr.stable.OperatingSystem; import com.fr.stable.StringUtils; -import net.sf.ehcache.util.NamedThreadFactory; import javax.swing.SwingConstants; import java.awt.Cursor; @@ -119,11 +119,12 @@ public class UserInfoLabel extends UILabel { * showBBSDialog 弹出BBS资讯框 */ public static void showBBSDialog() { - ThreadFactory namedThread = new NamedThreadFactory("bbs-dlg"); + ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() + .setNameFormat("bbs-dlg-thread-%s").build(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, - new LinkedBlockingQueue(1), namedThread); + new LinkedBlockingQueue(1), namedThreadFactory); threadPoolExecutor.execute(new Runnable() { @Override public void run() { diff --git a/designer/src/com/fr/start/Designer.java b/designer/src/com/fr/start/Designer.java index 71808c2b0..2f484553a 100644 --- a/designer/src/com/fr/start/Designer.java +++ b/designer/src/com/fr/start/Designer.java @@ -36,6 +36,7 @@ import com.fr.design.menu.SeparatorDef; import com.fr.design.menu.ShortCut; import com.fr.design.module.DesignModuleFactory; import com.fr.design.module.DesignerModule; +import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.design.utils.gui.GUICoreUtils; import com.fr.general.ComparatorUtils; import com.fr.general.Inter; @@ -43,7 +44,6 @@ import com.fr.stable.ProductConstants; import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; import com.fr.stable.xml.XMLTools; -import net.sf.ehcache.util.NamedThreadFactory; import javax.swing.JComponent; import javax.swing.JPanel; @@ -417,12 +417,13 @@ public class Designer extends BaseDesigner { int status = envManager.getActiveKeyStatus(); //没有联网验证过 if (status != 0) { - ThreadFactory namedThread = new NamedThreadFactory("net-verify"); + ThreadFactory namedThreadFactory = new ThreadFactoryBuilder() + .setNameFormat("net-verify-thread-%s").build(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(1), - namedThread); + namedThreadFactory); threadPoolExecutor.execute(new Runnable() { @Override public void run() { diff --git a/designer_base/src/com/fr/design/extra/PluginWebBridge.java b/designer_base/src/com/fr/design/extra/PluginWebBridge.java index 3de329f26..fed3c6855 100644 --- a/designer_base/src/com/fr/design/extra/PluginWebBridge.java +++ b/designer_base/src/com/fr/design/extra/PluginWebBridge.java @@ -7,12 +7,13 @@ import com.fr.design.bbs.BBSLoginUtils; import com.fr.design.dialog.UIDialog; import com.fr.design.extra.exe.GetPluginCategoriesExecutor; import com.fr.design.extra.exe.GetPluginFromStoreExecutor; +import com.fr.design.extra.exe.GetPluginPrefixExecutor; import com.fr.design.extra.exe.PluginLoginExecutor; import com.fr.design.extra.exe.ReadUpdateOnlineExecutor; import com.fr.design.extra.exe.SearchOnlineExecutor; import com.fr.design.extra.exe.callback.JSCallback; -import com.fr.design.extra.exe.GetPluginPrefixExecutor; import com.fr.design.gui.ilable.UILabel; +import com.fr.design.utils.concurrent.ThreadFactoryBuilder; import com.fr.general.FRLogger; import com.fr.general.Inter; import com.fr.general.SiteCenter; @@ -27,7 +28,6 @@ import javafx.concurrent.Task; import javafx.scene.web.WebEngine; import javafx.stage.FileChooser; import javafx.stage.Stage; -import net.sf.ehcache.util.NamedThreadFactory; import netscape.javascript.JSObject; import javax.swing.JDialog; @@ -51,7 +51,7 @@ import java.util.concurrent.TimeUnit; * 开放给Web组件的接口,用于安装,卸载,更新以及更改插件可用状态 */ public class PluginWebBridge { - private static final String PREFIX = "pluginbridge-pool"; + private static final String THREAD_NAME_TEMPLATE = "pluginbridge-thread-%s"; private static final String ACTION = "action"; private static final String KEYWORD = "keyword"; private static final int COREPOOLSIZE = 3; @@ -70,7 +70,7 @@ public class PluginWebBridge { private ExecutorService threadPoolExecutor = new ThreadPoolExecutor(COREPOOLSIZE, MAXPOOLSIZE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(COREPOOLSIZE), - new NamedThreadFactory(PREFIX)); + new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); /** * 动作枚举 diff --git a/designer_base/src/com/fr/design/utils/concurrent/ThreadFactoryBuilder.java b/designer_base/src/com/fr/design/utils/concurrent/ThreadFactoryBuilder.java new file mode 100644 index 000000000..d7d2393e8 --- /dev/null +++ b/designer_base/src/com/fr/design/utils/concurrent/ThreadFactoryBuilder.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package com.fr.design.utils.concurrent; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Locale; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A ThreadFactory builder, providing any combination of these features: + *
    + *
  • whether threads should be marked as {@linkplain Thread#setDaemon daemon} threads + *
  • a {@linkplain ThreadFactoryBuilder#setNameFormat naming format} + *
  • a {@linkplain Thread#setPriority thread priority} + *
  • an {@linkplain Thread#setUncaughtExceptionHandler uncaught exception handler} + *
  • a {@linkplain ThreadFactory#newThread backing thread factory} + *
+ *

If no backing thread factory is provided, a default backing thread factory is used as if by + * calling {@code setThreadFactory(}{@link Executors#defaultThreadFactory()}{@code )}. + * + * @author Kurt Alfred Kluever + * @since 4.0 + */ +public final class ThreadFactoryBuilder { + private String nameFormat = null; + private Boolean daemon = null; + private Integer priority = null; + private UncaughtExceptionHandler uncaughtExceptionHandler = null; + private ThreadFactory backingThreadFactory = null; + + /** + * Creates a new {@link ThreadFactory} builder. + */ + public ThreadFactoryBuilder() { + } + + /** + * Sets the naming format to use when naming threads ({@link Thread#setName}) which are created + * with this ThreadFactory. + * + * @param nameFormat a {@link String#format(String, Object...)}-compatible format String, to which + * a unique integer (0, 1, etc.) will be supplied as the single parameter. This integer will + * be unique to the built instance of the ThreadFactory and will be assigned sequentially. For + * example, {@code "rpc-pool-%d"} will generate thread names like {@code "rpc-pool-0"}, + * {@code "rpc-pool-1"}, {@code "rpc-pool-2"}, etc. + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setNameFormat(String nameFormat) { + String unused = format(nameFormat, 0); // fail fast if the format is bad or null + this.nameFormat = nameFormat; + return this; + } + + /** + * Sets daemon or not for new threads created with this ThreadFactory. + * + * @param daemon whether or not new Threads created with this ThreadFactory will be daemon threads + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setDaemon(boolean daemon) { + this.daemon = daemon; + return this; + } + + /** + * Sets the priority for new threads created with this ThreadFactory. + * + * @param priority the priority for new Threads created with this ThreadFactory + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setPriority(int priority) { + // Thread#setPriority() already checks for validity. These error messages + // are nicer though and will fail-fast. + + if (priority < Thread.MIN_PRIORITY) { + throw new IllegalArgumentException(format("Thread priority (%s) must be >= %s", priority, Thread.MIN_PRIORITY)); + } + if (priority > Thread.MAX_PRIORITY) { + throw new IllegalArgumentException(format("Thread priority (%s) must be <= %s", priority, Thread.MAX_PRIORITY)); + } + this.priority = priority; + return this; + } + + /** + * Sets the {@link UncaughtExceptionHandler} for new threads created with this ThreadFactory. + * + * @param uncaughtExceptionHandler the uncaught exception handler for new Threads created with + * this ThreadFactory + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setUncaughtExceptionHandler( + UncaughtExceptionHandler uncaughtExceptionHandler) { + if (uncaughtExceptionHandler == null) { + throw new NullPointerException(); + } + this.uncaughtExceptionHandler = uncaughtExceptionHandler; + return this; + } + + /** + * Sets the backing {@link ThreadFactory} for new threads created with this ThreadFactory. Threads + * will be created by invoking #newThread(Runnable) on this backing {@link ThreadFactory}. + * + * @param backingThreadFactory the backing {@link ThreadFactory} which will be delegated to during + * thread creation. + * @return this for the builder pattern + */ + public ThreadFactoryBuilder setThreadFactory(ThreadFactory backingThreadFactory) { + if (backingThreadFactory == null) { + throw new NullPointerException(); + } + this.backingThreadFactory = backingThreadFactory; + return this; + } + + /** + * Returns a new thread factory using the options supplied during the building process. After + * building, it is still possible to change the options used to build the ThreadFactory and/or + * build again. State is not shared amongst built instances. + * + * @return the fully constructed {@link ThreadFactory} + */ + public ThreadFactory build() { + return doBuild(this); + } + + // Split out so that the anonymous ThreadFactory can't contain a reference back to the builder. + // At least, I assume that's why. TODO(cpovirk): Check, and maybe add a test for this. + private static ThreadFactory doBuild(ThreadFactoryBuilder builder) { + final String nameFormat = builder.nameFormat; + final Boolean daemon = builder.daemon; + final Integer priority = builder.priority; + final UncaughtExceptionHandler uncaughtExceptionHandler = builder.uncaughtExceptionHandler; + final ThreadFactory backingThreadFactory = + (builder.backingThreadFactory != null) + ? builder.backingThreadFactory + : Executors.defaultThreadFactory(); + final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; + return new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = backingThreadFactory.newThread(runnable); + if (nameFormat != null) { + thread.setName(format(nameFormat, count.getAndIncrement())); + } + if (daemon != null) { + thread.setDaemon(daemon); + } + if (priority != null) { + thread.setPriority(priority); + } + if (uncaughtExceptionHandler != null) { + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); + } + return thread; + } + }; + } + + private static String format(String format, Object... args) { + return String.format(Locale.ROOT, format, args); + } +}