我有一个Java应用程序,在tomcat上运行Spring。
这堂课给我带来了一个非常奇怪的问题:
@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,那么我没有例外,一切正常。任何人都可以告诉我为什么这个默认或私有构造函数导致运行时异常?
答案 0 :(得分:4)
Tomcat使用Class.newInstance()
创建ThreadPool
的实例。该方法遵循Java的访问规则。
由于您的构造函数是私有的,因此失败并显示IllegalAccessException
。这是运行时等效于不允许将函数调用到编译器错误,如果您尝试在new ThreadPool()
之外编写ThreadPool
,则会看到
答案 1 :(得分:2)
Tomcat' org.apache.catalina.core.DefaultInstanceManager
正在尝试创建您已配置为上下文侦听器的ThreadPool
对象。现在,由于这不在org.apache.catalina.core
之内,您必须使用公共构造函数,否则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;
}
答案 2 :(得分:1)
通过错误,它说清楚,因为它无法访问类的成员。
can not access a member of class com.testApp.util.ThreadPool with modifiers
答案 3 :(得分:0)
我想我意外地发现了我获得例外的真正原因。目前我正在使用这个类,没有抛出异常,在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);
我再次获得异常,因为waitingTasks
在我创建新的TrackingThreadPool
实例时未实例化。
我猜你可以有一个ThreadPoolExecutor
的子类,带有私有构造函数/ singelton模式。