非 public 构造函数导致 Tomcat7 出现问题?
Non-public constructor causing problems with Tomcat7?
我在 tomcat 上有 Java 应用 Spring 运行。
这个 class 给我带来了一个非常奇怪的问题:
@WebListener
public class ThreadPool extends ThreadPoolExecutor implements ServletContextListener {
private ThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_TIMEPERIOD, TimeUnit.SECONDS, taskQueue);
}
private static final ThreadPoolExecutor pool = new ThreadPool();
public synchronized static void submit(Task task) {
executingTasks.add(task);
pool.execute(task);
}
@Override
public synchronized void contextDestroyed(ServletContextEvent arg0) {
cancelWaitingTasks();
sendStopSignalsToExecutingTasks();
pool.shutdown();
}
...
}
如果构造函数是私有的或默认的,我会在运行时遇到此异常(在对应用程序的第一个 HTTP 请求时):
Error configuring application listener of class com.testApp.util.ThreadPool
java.lang.IllegalAccessException: Class org.apache.catalina.core.DefaultInstanceManager can not access a member of class com.testApp.util.ThreadPool with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:140)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Skipped installing application listeners due to previous error(s)
Error listenerStart
Context [] startup failed due to previous errors
但是,如果我设置构造函数 public,那么我不会出现任何异常,并且一切正常。谁能告诉我为什么这个默认或私有构造函数会导致运行时异常?
通过报错,说的很清楚是因为无法访问class的成员。
can not access a member of class com.testApp.util.ThreadPool with modifiers
Tomcat 使用 Class.newInstance()
创建您的 ThreadPool
的实例。此方法遵守 Java 的访问规则。
由于您的构造函数是私有的,因此它失败并显示 IllegalAccessException
。这是运行时等效于不允许调用编译器错误的函数,如果您尝试在 ThreadPool
、
之外编写 new ThreadPool()
,您会看到该错误
Tomcat 的 org.apache.catalina.core.DefaultInstanceManager
正在尝试创建您的 ThreadPool
的对象,您已将其配置为上下文侦听器。现在,由于这是在 org.apache.catalina.core
之外,您必须使用 public 构造函数,否则 org.apache.catalina.core.DefaultInstanceManager
将无法创建其对象。
来自org.apache.catalina.core.DefaultInstanceManager
private Object newInstance(Object instance, Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException {
if (!ignoreAnnotations) {
Map<String, String> injections = injectionMap.get(clazz.getName());
processAnnotations(instance, injections);
postConstruct(instance, clazz);
}
return instance;
}
我想我无意中发现了出现异常的真正原因。目前我正在使用这个 class,没有抛出异常,在 GlassFish 和 Tomcat:
上测试
public class TrackingThreadPool extends ThreadPoolExecutor {
private static final int MAX_WAITING_TASKS = 4000;
private static final int MAX_ACTIVE_THREADS = 20;
private static final int MIN_ACTIVE_THREADS = 4;
private static final int DEACTIVATE_THREADS_AFTER_SECONDS = 60;
private TrackingThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_SECONDS,
TimeUnit.SECONDS, waitingTasks);
}
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final TrackingThreadPool instance = new TrackingThreadPool();
public synchronized static void submitAndTrack(Task task) {
executingTasks.put(task.getId(), task);
instance.execute(task);
}
public synchronized static void shutdownAndCancelAllTasks() {
cancelWaitingTasks();
sendStopSignalToExecutingTasks();
instance.shutdown();
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (r instanceof Task) {
executingTasks.remove(((Task) r).getId());
}
}
private static void cancelWaitingTasks() {
List<Runnable> waitingTaskListRunnables = new ArrayList<>(waitingTasks.size() + 10); //+10 to avoid resizing
waitingTasks.drainTo(waitingTaskListRunnables);
for (Runnable r : waitingTaskListRunnables) {
if (r instanceof Task) {
((Task) r).sendStopSignal(byShutdownMethod());
}
}
}
private static void sendStopSignalToExecutingTasks() {
for (long taskId : executingTasks.keySet()) {
Task executingTask = executingTasks.get(taskId);
executingTask.sendStopSignal(byShutdownMethod());
}
}
private static String byShutdownMethod() {
return TrackingThreadPool.class.getSimpleName() + "#shutdownAndCancelAllTasks()";
}
}
如果我像这样交换 BlockingQueue<Runnable> waitingTasks
和 TrackingThreadPool instance
的位置:
private static final TrackingThreadPool instance = new TrackingThreadPool();
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
我再次遇到异常,因为在我创建新的 TrackingThreadPool
实例时 waitingTasks
尚未实例化。
我想你可以有一个 ThreadPoolExecutor
的子 class 和一个私有构造函数/singelton 模式。
我在 tomcat 上有 Java 应用 Spring 运行。
这个 class 给我带来了一个非常奇怪的问题:
@WebListener
public class ThreadPool extends ThreadPoolExecutor implements ServletContextListener {
private ThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_TIMEPERIOD, TimeUnit.SECONDS, taskQueue);
}
private static final ThreadPoolExecutor pool = new ThreadPool();
public synchronized static void submit(Task task) {
executingTasks.add(task);
pool.execute(task);
}
@Override
public synchronized void contextDestroyed(ServletContextEvent arg0) {
cancelWaitingTasks();
sendStopSignalsToExecutingTasks();
pool.shutdown();
}
...
}
如果构造函数是私有的或默认的,我会在运行时遇到此异常(在对应用程序的第一个 HTTP 请求时):
Error configuring application listener of class com.testApp.util.ThreadPool
java.lang.IllegalAccessException: Class org.apache.catalina.core.DefaultInstanceManager can not access a member of class com.testApp.util.ThreadPool with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:140)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Skipped installing application listeners due to previous error(s)
Error listenerStart
Context [] startup failed due to previous errors
但是,如果我设置构造函数 public,那么我不会出现任何异常,并且一切正常。谁能告诉我为什么这个默认或私有构造函数会导致运行时异常?
通过报错,说的很清楚是因为无法访问class的成员。
can not access a member of class com.testApp.util.ThreadPool with modifiers
Tomcat 使用 Class.newInstance()
创建您的 ThreadPool
的实例。此方法遵守 Java 的访问规则。
由于您的构造函数是私有的,因此它失败并显示 IllegalAccessException
。这是运行时等效于不允许调用编译器错误的函数,如果您尝试在 ThreadPool
、
new ThreadPool()
,您会看到该错误
Tomcat 的 org.apache.catalina.core.DefaultInstanceManager
正在尝试创建您的 ThreadPool
的对象,您已将其配置为上下文侦听器。现在,由于这是在 org.apache.catalina.core
之外,您必须使用 public 构造函数,否则 org.apache.catalina.core.DefaultInstanceManager
将无法创建其对象。
来自org.apache.catalina.core.DefaultInstanceManager
private Object newInstance(Object instance, Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException {
if (!ignoreAnnotations) {
Map<String, String> injections = injectionMap.get(clazz.getName());
processAnnotations(instance, injections);
postConstruct(instance, clazz);
}
return instance;
}
我想我无意中发现了出现异常的真正原因。目前我正在使用这个 class,没有抛出异常,在 GlassFish 和 Tomcat:
上测试public class TrackingThreadPool extends ThreadPoolExecutor {
private static final int MAX_WAITING_TASKS = 4000;
private static final int MAX_ACTIVE_THREADS = 20;
private static final int MIN_ACTIVE_THREADS = 4;
private static final int DEACTIVATE_THREADS_AFTER_SECONDS = 60;
private TrackingThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_SECONDS,
TimeUnit.SECONDS, waitingTasks);
}
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final TrackingThreadPool instance = new TrackingThreadPool();
public synchronized static void submitAndTrack(Task task) {
executingTasks.put(task.getId(), task);
instance.execute(task);
}
public synchronized static void shutdownAndCancelAllTasks() {
cancelWaitingTasks();
sendStopSignalToExecutingTasks();
instance.shutdown();
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (r instanceof Task) {
executingTasks.remove(((Task) r).getId());
}
}
private static void cancelWaitingTasks() {
List<Runnable> waitingTaskListRunnables = new ArrayList<>(waitingTasks.size() + 10); //+10 to avoid resizing
waitingTasks.drainTo(waitingTaskListRunnables);
for (Runnable r : waitingTaskListRunnables) {
if (r instanceof Task) {
((Task) r).sendStopSignal(byShutdownMethod());
}
}
}
private static void sendStopSignalToExecutingTasks() {
for (long taskId : executingTasks.keySet()) {
Task executingTask = executingTasks.get(taskId);
executingTask.sendStopSignal(byShutdownMethod());
}
}
private static String byShutdownMethod() {
return TrackingThreadPool.class.getSimpleName() + "#shutdownAndCancelAllTasks()";
}
}
如果我像这样交换 BlockingQueue<Runnable> waitingTasks
和 TrackingThreadPool instance
的位置:
private static final TrackingThreadPool instance = new TrackingThreadPool();
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
我再次遇到异常,因为在我创建新的 TrackingThreadPool
实例时 waitingTasks
尚未实例化。
我想你可以有一个 ThreadPoolExecutor
的子 class 和一个私有构造函数/singelton 模式。