So, I have a non-looping Thread that I want to stop. I searched the internet and did not find a solution without creating a looping Thread.
The Thread requests information from a web server and displays the results in a JList. It does not loop forever. The reason why I made this function a Thread is because it takes about 1-5 minutes depending on how much information are send from the web server and I want to continue working in the application without it being blocked by the request. The Thread looks like this:
public class Search extends Thread implements Runnable{
#Override
public void run(){
requestToWebServer(); //10-20sec
receiveInformation(); //10-20sec
handleResponse(); //1-4min
addDataToJList(); //5sec
}
}
Now my problem is that I do not see any way to stop this Thread from running completely. I don't want to use the deprecated method Thread.stop.
Edit:
Complete code in run method:
public void run(){
try{
log.info(Languages.getString("SearchFrame.2") + this.query); //$NON-NLS-1$
String resp = ConnectionUtils.request("http://hdfilme.tv/movie/search?key=" //$NON-NLS-1$
+ this.query.replaceAll(" ", "+")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Matcher movie_img_matcher = MOVIE_IMG_PATTERN.matcher(resp);
Matcher movie_stream_matcher = MOVIE_STREAM_PATTERN.matcher(resp);
log.info(Languages.getString("SearchFrame.4")); //$NON-NLS-1$
while (movie_img_matcher.find()) {
img_src.add(new URL(movie_img_matcher.group(1)));
names.add(movie_img_matcher.group(2));
}
log.info(Languages.getString("SearchFrame.5") + img_src.size() + Languages.getString("SearchFrame.6")); //$NON-NLS-1$ //$NON-NLS-2$
log.info(Languages.getString("SearchFrame.7")); //$NON-NLS-1$
while (movie_stream_matcher.find() && mov_src.size() < img_src.size()) {
mov_src.add(new URL(movie_stream_matcher.group(1) + "stream")); //$NON-NLS-1$
}
log.info(Languages.getString("SearchFrame.5") + mov_src.size() + Languages.getString("SearchFrame.8")); //$NON-NLS-1$ //$NON-NLS-2$
log.info(Languages.getString("SearchFrame.9")); //$NON-NLS-1$
for (int i = 0; i < mov_src.size(); i++) {
log.debug(Languages.getString("SearchFrame.10")); //$NON-NLS-1$
String response = ConnectionUtils.request(mov_src.get(i).toString());
ConnectionUtils.getLastPage();
Matcher movie_link_matcher = MOVIE_LINK_PATTERN.matcher(response);
log.debug(Languages.getString("SearchFrame.11")); //$NON-NLS-1$
if (movie_link_matcher.find()) {
res.add(movie_link_matcher.group(2));
con = getCon(new URL(movie_link_matcher.group(1).replaceAll("\\\\/", "/")), "HEAD");
log.debug(Languages.getString("SearchFrame.12")); //$NON-NLS-1$
last_modified.add(con.getHeaderField("Last-Modified")); //$NON-NLS-1$
sizes.add(i, con.getContentLengthLong());
}
if(Options.getString("DefaultDownload").equals("0")){
dirs.add(i, System.getProperty("user.home") + "\\Videos"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
dirs.add(i, Options.getString("DefaultDownload"));
}
}
log.debug(Languages.getString("SearchFrame.13")); //$NON-NLS-1$
for(int i = 0; i < mov_src.size(); i++){
try{
model.addElement(new Movie(mov_src.get(i), img_src.get(i), names.get(i), res.get(i), dirs.get(i), last_modified.get(i), sizes.get(i)));
} catch(IndexOutOfBoundsException ex){
log.info(ex);
}
}
} catch(Exception ex){
log.error(ex);
}
}
You will have to insert in your code into each loop iteration and in some other places if you wish a check with method invocation isInterrupted() or interrupted() and if the thread has been interrupted just return from your thread. In your parent thread you need to invoke interrupt() method on the thread that you want to stop. Then next time your child-thread will check if it was interrupted it will know to return. Please read more on mentioned methods in java doc for class Thread. The only safe way to kill thread is to ask the Thread itself to stop. Any other way is inherently dangerous. That is why method stop() has been depricated
Related
I am trying to update a large (~150,000 line) Java/Eclipse program from 2009 that has several threads, and communicates with a TCP-IP telescope. I wrote new code to do the communications, and that all works fine when separately tested. This is on a Mac - the original worked on Mac OSX 10.6 and nothing later - I'm writing this for current Mac OS 12.xx
In the overall program, there is a thread that receives mouse clicks from the GUI and initiates operation of a thread with runs the communications code. The communications thread stays alive, using parameters passed to it to determine which communication function to execute.
Here's some of the code:
class ActualTask {
ActualTask () {
// PRAA Dec 7 2021
System.out.println("What thread are we in? it's: "+ Thread.currentThread().getName());
while (true) // this is true so we can keep reusing this task!
{
// PRAA Dec 7 2021
//System.out.println("What thread are we in? it's: "+ Thread.currentThread().getName());
if (running)
{
// PRAA Dec 7 2021
System.out.println("Just before call to run() - What thread are we in? it's: "+ Thread.currentThread().getName());
run();
// PRAA Dec 7 2021
System.out.println("What thread are we in? it's: "+ Thread.currentThread().getName());
running = false;
}
}
}
// ------------------------------------
void run()
{
done = false;
successful = true;
telescopeException = null;
// PRAA Dec 7 2021
System.out.println("In run() - thread is: "+ Thread.currentThread().getName());
try
{ // *** execute the telescope task ***
TelescopeGlobals.telescopeProgressTaskData.setBackgroundColor(Color.red); // red when active
telescopeTask.execute();
done = true;
TelescopeGlobals.telescopeProgressTaskData.setBackgroundColor(Color.white); // white when NOT active
}
catch (TelescopeException te)
{
// Clear progress bar before throwing up dialog
TelescopeGlobals.telescopeProgressTaskData.setBackgroundColor(Color.white); // white when NOT active
TelescopeGlobals.telescopeProgressTaskData.clear();
done = true;
successful = false;
telescopeException = te;
Reporting.selctiveAlert(te.getMessage() + "\n" + te.getWhereExceptionOccured());
TelescopeGlobals.telescopeProgressTaskData.setText1("Error: " + te.getMessage());
TelescopeGlobals.telescopeProgressTaskData.setText2("Where: " + te.getWhereExceptionOccured());
}
done = true;
}
}
When I am in "debug" mode, and I single-step through the communications code, things usually execute properly - however, when I hit "Run", things don't go so well -
the first call works ok
the second call appears to do nothing - that is, it doesn't seem to be executing, and none of my print statements print
subsequent calls only print an error message saying the previous task has not completed.
So I'm wondering - what is the difference between the "run" and "debug" modes - why would one work and the other behave differently - and
Does all of the "SwingWorker" stuff from 2009 still work?
Should creating the communications thread be done differently? It is just created once, as shown in the code snippet below:
// Static member above of HighLevelTaskWorker makes sure that one command is dispatched to the telescope at a time.
class HighLevelTaskWorker { // SwingWorker wrapper class
private lib.SwingWorker worker = null;
private boolean done;
private boolean successful;
private TelescopeException telescopeException;
private boolean running;
private TelescopeTask telescopeTask;
public HighLevelTaskWorker()
{
}
public void start(TelescopeTask telescopeTask) {
this.telescopeTask = telescopeTask;
// PRAA Dec 7 2021
System.out.println("What thread are we in? it's: "+ Thread.currentThread().getName());
done = false;
successful = false;
telescopeException = null;
running = true;
if (worker == null) // no need to create this tread over and over again, think of all that Garbage Collecting and tread creation!
{
worker = new lib.SwingWorker() {
public Object construct() {
return new ActualTask();
}
};
worker.start();
}
}
Thanks so much for any help !!!
From loom-lab, given the code
var virtualThreadFactory = Thread.ofVirtual().factory();
try (var executorService = Executors.newThreadPerTaskExecutor(virtualThreadFactory)) {
IntStream.range(0, 15).forEach(item -> {
executorService.submit(() -> {
try {
var milliseconds = item * 1000;
System.out.println(Thread.currentThread() + " sleeping " + milliseconds + " milliseconds");
Thread.sleep(milliseconds);
System.out.println(Thread.currentThread() + " awake");
if (item == 8) throw new RuntimeException("task 8 is acting up");
} catch (InterruptedException e) {
System.out.println("Interrupted task = " + item + ", Thread ID = " + Thread.currentThread());
}
});
});
}
catch (RuntimeException e) {
System.err.println(e.getMessage());
}
My hope was that the code would catch the RuntimeException and print the message, but it does not.
Am I hoping for too much, or will this someday work as I hope?
In response to an amazing answer by Stephen C, which I can fully appreciate, upon further exploration I discovered via
static String spawn(
ExecutorService executorService,
Callable<String> callable,
Consumer<Future<String>> consumer
) throws Exception {
try {
var result = executorService.submit(callable);
consumer.accept(result);
return result.get(3, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
// The timeout expired...
return callable.call() + " - TimeoutException";
}
catch (ExecutionException e) {
// Why doesn't malcontent get caught here?
return callable.call() + " - ExecutionException";
}
catch (CancellationException e) { // future.cancel(false);
// Exception was thrown
return callable.call() + " - CancellationException";
}
catch (InterruptedException e) { // future.cancel(true);
return callable.call() + "- InterruptedException ";
}
}
and
try (var executorService = Executors.newThreadPerTaskExecutor(threadFactory)) {
Callable<String> malcontent = () -> {
Thread.sleep(Duration.ofSeconds(2));
throw new IllegalStateException("malcontent acting up");
};
System.out.println("\n\nresult = " + spawn(executorService, malcontent, (future) -> {}));
} catch (Exception e) {
e.printStackTrace(); // malcontent gets caught here
}
I was expecting malcontent to get caught in spawn as an ExecutionException per the documentation, but it does not. Consequently, I have trouble reasoning about my expectations.
Much of my hope for Project Loom was that, unlike Functional Reactive Programming, I could once again rely on Exceptions to do the right thing, and reason about them such that I could predict what would happen without having to run experiments to validate what really happens.
As Steve Jobs (at NeXT) used to say: "It just works"
So far, my posting on loom-dev#openjdk.java.net has not been responded to... which is why I have used StackOverflow. I don't know the best way to engage the Project Loom developers.
This is speculation ... but I don't think so.
According to the provisional javadocs, ExecutorService now inherits AutoClosable, and it is specified that the default behavior of the close() method is to perform a clean shutdown and wait for it to complete. (Note that this is described as default behavior not required behavior!)
So why couldn't they change the behavior to catch an resignal the exceptions on this thread's stack?
One problem is that specifying patterns of behavior that are logically consistent for both this case, and the case where the ExecutorService is not used as a resource in a try with resources. In order to implement the behavior in this case, the close() method has to be informed by some other part of the executor service of the task's unhandled exception. But if nothing calls close() then the exceptions can't be re-raised. And if the close() is called in a finalizer or similar, there probably won't be anything to handle them. At the very least, it is complicated.
A second problem is that it would be difficult to handle the exception(s) in the general case. What if more than one task failed with an exception? What if different tasks failed with different exceptions? How does the code that handles the exception (e.g. your catch (RuntimeException e) ... figure out which task failed?
A third problem is that this would be a breaking change. In Java 17 and earlier, the above code would not propagate any exceptions from the tasks. In Java 18 and later it would. Java 17 code that assumed there were no "random" exceptions from failed tasks delivered to this thread would break.
A fourth point is that this would be an nuisance in use-cases where the Java 18+ programmer wants to treat the executor service as a resource, but does not want to deal with "stray" exceptions on this thread. (I suspect that would be the majority of use-cases for autoclosing an executor service.)
A fifth problem (if you want to call it that) is that it is a breaking change for early adopters of Loom. (I am reading your question as saying that you tried it with Loom and it currently doesn't behave as you proposed.)
The final problem is that there are already ways to capture a task's exception and deliver it; e.g. via the Future objects returned when you submit a task. This proposal is not filling a gap in ExecutorService functionality.
(Phew!)
Of course I don't know that the Java developers will actually do. And we won't collectively know until Loom is finally released as a non-preview feature of mainstream Java.
Anyhow, if you want to lobby for this, you should email the Loom mailing list about it.
LOOM has made many improvements such as making ExecutorService an AutoClosable so it simplifies coding, eliminating calls to shutdown / awaitTermination.
Your point on the expectation of neat exception handling applies to typical usage of ExecutorService in any JDK - not just the upcoming LOOM release - so IMO isn't obviously necessary to be tied in with LOOM work.
The error handling you wish for is quite easy to incorporate with any version of JDK by adding a few lines of code around code blocks that use ExecutorService:
var ex = new AtomicReference<RuntimeException>();
try {
// add any use of ExecutorService here
// eg OLD JDK style:
// var executorService = Executors.newFixedThreadPool(5);
try (var executorService = Executors.newThreadPerTaskExecutor(virtualThreadFactory)) {
...
if (item == 8) {
// Save exception before sending:
ex.set(new RuntimeException("task 8 is acting up"));
throw ex.get();
}
...
}
// OR: not-LOOM JDK call executorService.shutdown/awaitTermination here
// Pass on any handling problem
if (ex.get() != null)
throw ex.get();
}
catch (Exception e) {
System.err.println("Exception was: "+e.getMessage());
}
Not elegant as you hope for, but works in any JDK release.
EDIT On your edited question:
You've put callable.call() as the code inside catch (ExecutionException e) { so that you've lost the first exception and malcontent raises a second exception. Add System.out.println to see the original:
catch (ExecutionException e) {
System.out.println(Thread.currentThread()+" ExecutionException: "+e);
e.printStackTrace();
// Why doesn't malcontent get caught here?
return callable.call() + " - ExecutionException";
}
I think, the closest to what you are trying to achieve, is
try(var executor = StructuredExecutor.open()) {
var handler = new StructuredExecutor.ShutdownOnFailure();
IntStream.range(0, 15).forEach(item -> {
executor.fork(() -> {
var milliseconds = item * 100;
System.out.println(Thread.currentThread()
+ "sleeping " + milliseconds + " milliseconds");
Thread.sleep(milliseconds);
System.out.println(Thread.currentThread() + " awake");
if(item == 8) {
throw new RuntimeException("task 8 is acting up");
}
return null;
}, handler);
});
executor.join();
handler.throwIfFailed();
}
catch(InterruptedException|ExecutionException ex) {
System.err.println("Caught in initiator thread");
ex.printStackTrace();
}
which will run all jobs in virtual threads and generate an exception in the initiator thread when one of the jobs failed. StructuredExecutor is a new tool introduced by project Loom which allows to show the ownership of the created virtual threads to this specific job in diagnostic tools. But note that it’s close() won’t wait for the completion but rather requires the owner to do this before closing, throwing an exception if the developer failed to do so.
The behavior of classic ExecutorService implementations won’t change.
A solution for the ExecutorService would be
try(var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var jobs = executor.invokeAll(IntStream.range(0, 15).<Callable<?>>mapToObj(item ->
() -> {
var milliseconds = item * 100;
System.out.println(Thread.currentThread()
+ " sleeping " + milliseconds + " milliseconds");
Thread.sleep(milliseconds);
System.out.println(Thread.currentThread() + " awake");
if(item == 8) {
throw new RuntimeException("task 8 is acting up");
}
return null;
}).toList());
for(var f: jobs) f.get();
}
catch(InterruptedException|ExecutionException ex) {
System.err.println("Caught in initiator thread");
ex.printStackTrace();
}
Note that while invokeAll waits for the completion of all jobs, we still need the loop calling get to enforce an ExecutionException to be thrown in the initiating thread.
I have a scheduled executor to reset a parameter to 0 and awake all active threads to continue processing. However after initial run of the thread it is not executing again.
ScheduledExecutorService exec = Executors.newScheduledThreadPool(4);
exec.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
logger.info("Setting hourly limit record count back to 0 to continue processing");
lines = 0;
executor.notifyAll();
Thread.currentThread().interrupt();
return;
}
}, 0, 1, TimeUnit.MINUTES);
There is another Executor defined in the class which executes further processes and not sure if this influences it:
ExecutorService executor = Executors.newCachedThreadPool();
for (String processList : processFiles) {
String appName = processList.substring(0,processList.indexOf("-"));
String scope = processList.substring(processList.lastIndexOf("-") + 1);
logger.info("Starting execution of thread for app " + appName + " under scope: " + scope);
try {
File processedFile = new File(ConfigurationReader.processedDirectory + appName + "-" + scope + ".csv");
processedFile.createNewFile();
executor.execute(new APIInitialisation(appName,processedFile.length(),scope));
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
From the documentation for ScheduledExecutorService.scheduleAtFixedRate():
If any execution of the task encounters an exception, subsequent executions are suppressed.
So something in your task is throwing an exception. I would guess the call to executor.notifyAll() which is documented to throw an IllegalMonitorStateException:
if the current thread is not the owner of this object's monitor.
Your scheduled task will most probably end up in a uncaught Exception. Taken from the JavaDoc of ScheduledExecutorService.scheduleAtFixedRate
If any execution of the task encounters an exception, subsequent
executions are suppressed.
Because you are provoking a uncaught exception, all further executions are cancelled.
I have a task where while generating a random password for user the SMS should go after 4 MIN, but the welcome SMS should go immediately. Since password I am setting first and need to send after 4 MIN I am making that thread sleep (Cant use ExecutorServices), and welcome SMS thread start.
Here is the code:
String PasswordSMS="Dear User, Your password is "+'"'+"goody"+'"'+" Your FREE
recharge service is LIVE now!";
String welcomeSMS="Dear goody, Welcome to XYZ";
try {
Thread q=new Thread(new GupShupSMSUtill(PasswordSMS,MOB_NUM));
Thread.sleep(4 * 60 * 1000);
q.start();
GupShupSMSUtill sendWelcomesms2=new GupShupSMSUtill(welcomeSMS, MOB_NUM);
Thread Bal3=new Thread(sendWelcomesms2);
Bal3.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
</code>
So if I change the order the thread sendWelcomesms2 Immediately starts.I have to send welcome SMS then password sms (After 4 Min) how its achievable ??
NOTE: Both SMS come after 4 MIN
Thread.sleep(4 * 60 * 1000);
delays execution of your currently running thread, your q.start() is not executed until the wait time is over. This order doesn't make sense.
Your thread is only created when
Thread q=new Thread(new GupShupSMSUtill(PasswordSMS,MOB_NUM));
is executed. Your thread is started when
q.start();
is executed. So if you want to achieve running the q thread while the main thread sleep, you should write your lines in this order:
Thread q=new Thread(new GupShupSMSUtill(PasswordSMS,MOB_NUM)); // Create thread
q.start(); // start thread
Thread.sleep(4 * 60 * 1000); // suspend main thread for 4 sec
You can use join():
String PasswordSMS = "Dear User, Your password is " + "\"" + "goody" + "\"" + " Your FREE recharge service is LIVE now!";
String welcomeSMS = "Dear goody, Welcome to XYZ";
try
{
GupShupSMSUtill sendWelcomesms2 = new GupShupSMSUtill(welcomeSMS, MOB_NUM);
Thread Bal3 = new Thread(sendWelcomesms2);
Bal3.start();
Thread q = new Thread(new GupShupSMSUtill(PasswordSMS, MOB_NUM));
q.start();
q.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
Or latch:
private static java.util.concurrent.CountDownLatch latch = new java.util.concurrent.CountDownLatch(1);
And the code:
String PasswordSMS = "Dear User, Your password is " + "\"" + "goody" + "\"" + " Your FREE recharge service is LIVE now!";
String welcomeSMS = "Dear goody, Welcome to XYZ";
try
{
GupShupSMSUtill sendWelcomesms2 = new GupShupSMSUtill(welcomeSMS, MOB_NUM);
Thread Bal3 = new Thread(sendWelcomesms2);
Bal3.start();
Thread q = new Thread(new GupShupSMSUtill(PasswordSMS, MOB_NUM));
q.start();
latch.await(); // Wait
}
catch (InterruptedException e)
{
e.printStackTrace();
}
At the end of the Thread "q":
latch.countDown(); // stop to wait
Hint - Don't use Thread.sleep(x) in this case.
You are sleeping the current thread, before you issue the startcommand for q.
You probably want to issue the sleep inside GupShupSMSUtill() (maybe change its signature to something like GupShupSMSUtill(PasswordSMS,MOB_NUM, sleeptime) to be able to control how long it sleeps).
Whenever I stop or redeploy the webapp, I see lot of errors similar to,
msg=The web application [] created a ThreadLocal with key of type [] (value []) and
a value of type [] (value []) but failed to remove it when the web application was
stopped. Threads are going to be renewed over time to try and avoid probable memory leak
I'm not creating any ThreadLocals in my app but referencing many libraries which may be creating these ThreadLocals. We are currently using Tomcat 7. I've already gone through other similar questions [Memory leak when redeploying application in Tomcat or What are these warnings in catalina.out?] but all of them only suggest that this is Tomcat feature to warn you about ThreadLocals not being removed. I don't see any answer to remove ThreadLocals. I also see few ERRORs regarding thread not stopped as well,
msg=The web application [] appears to have started a thread named [] but has
failed to stop it. This is very likely to create a memory leak.
These are being logged in as ERRORs in our company's central logging system and thereby increasing the error count by our application. This certainly does not look good when we check the performance of our app. I tried the implementations from these two sources [Killing threads and Sample code from this thread], but doesn't seem to work. It removes thread/threadlocals not created by our app. What I need is to remove only the threads/threadlocals started by our webapp. Is there any way we can remove these in contextDestroyed() method of ServletContextListener? Following is my current ServletContextListener class,
public class CustomServletContextListener implements ServletContextListener {
private List<String> threadsAtStartup;
#Override
public void contextInitialized(ServletContextEvent sce) {
retrieveThreadsOnStartup();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
// Now deregister JDBC drivers in this context's ClassLoader:
// Get the webapp's ClassLoader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
// Loop through all drivers
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
if (driver.getClass().getClassLoader() == cl) {
// This driver was registered by the webapp's ClassLoader, so deregister it:
try {
System.out.println("Deregistering JDBC driver {}: " + driver);
DriverManager.deregisterDriver(driver);
} catch (SQLException ex) {
System.out.println("Error deregistering JDBC driver {}: " + driver + "\nException: " + ex);
}
} else {
// driver was not registered by the webapp's ClassLoader and may be in use elsewhere
System.out.println("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader: " + driver);
}
}
//Threads
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
threadGroup = Thread.currentThread().getThreadGroup();
Thread[] threads;
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e.getMessage());
return;
} catch (IllegalAccessException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e.getMessage());
return;
}
int toBeKilledCount = 0;
int totalThreadCount = 0;
int killedTLCount = 0;
int totalTLCount = 0;
int killedITLCount = 0;
int totalITLCount = 0;
for (; totalThreadCount < threads.length; totalThreadCount++) {
Thread thread = threads[totalThreadCount];
if(thread != null) {
String threadName = thread.getName();
boolean shouldThisThreadBeKilled;
shouldThisThreadBeKilled = isThisThreadToBeKilled(Thread.currentThread(), thread);
if (shouldThisThreadBeKilled) {
//ThreadLocal
try {
removeThreadLocals("threadLocals", thread);
removeThreadLocals("inheritableThreadLocals", thread);
} catch (Exception e) {
System.out.println("\tError accessing threadLocals field of '" + threadName + "': " + e.getMessage());
}
//Stop thread
thread.interrupt();
thread = null;
toBeKilledCount++;
}
}
}
}
private void retrieveThreadsOnStartup() {
final Thread[] threads;
final ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
try {
threads = retrieveCurrentActiveThreads(threadGroup);
} catch (NoSuchFieldException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e);
threadsAtStartup = new ArrayList<String>();
return;
} catch (IllegalAccessException e) {
System.out.println("Could not retrieve initial Threads list. The application may be unstable on shutting down " + e);
threadsAtStartup = new ArrayList<String>();
return;
}
threadsAtStartup = new ArrayList<String>(threads.length);
for (int i = 0; i < threads.length; i++) {
final Thread thread;
try {
thread = threads[i];
if (null != thread) {
threadsAtStartup.add(thread.getName());
}
} catch (RuntimeException e) {
System.out.println("An error occured on initial Thread statement: " + e);
}
}
}
private Thread[] retrieveCurrentActiveThreads(ThreadGroup threadGroup) throws NoSuchFieldException, IllegalAccessException {
final Thread[] threads;
final Field privateThreadsField;
privateThreadsField = ThreadGroup.class.getDeclaredField("childrenThreads");
privateThreadsField.setAccessible(true);
threads = (Thread[]) privateThreadsField.get(threadGroup);
return threads;
}
private void removeThreadLocals(String fieldName, Thread thread) {
Field threadLocalsField = Thread.class.getDeclaredField(fieldName);
threadLocalsField.setAccessible(true);
Object threadLocalMap = threadLocalsField.get(thread);
Field tableField = threadLocalMap.getClass().getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalMap);
int count = 0;
for (int i = 0, length = Array.getLength(table); i < length; ++i) {
Object entry = Array.get(table, i);
if (entry != null) {
totalTLCount++;
Object threadLocal = ((WeakReference)entry).get();
if (threadLocal != null) {
Array.set(table, i, null);
killedTLCount++;
}
}
}
}
private Boolean isThisThreadToBeKilled(Thread currentThread, Thread testThread) {
boolean toBeKilled;
String currentThreadName = currentThread.getName();
String testThreadName = testThread.getName();
System.out.println("currentThreadName: " + currentThreadName + ", testThreadName: " + testThreadName);
return !threadsAtStartup.contains(testThreadName) // this thread was not already running at startup
&& !testThreadName.equalsIgnoreCase(currentThreadName); // this is not the currently running thread
}
}
Update: I'm still not able to resolve this. Any help? Nobody ever ran into these?
There is no solution to fix all threadlocal leaks in one go.
Normally third party libraries using Threadlocal variables have some kind of cleanup API call which can be used to clear their local thread variables.
You have to check for all reported threadlocal leaks and find the correct way of disposing them in the corresponding library. You can do this in your CustomServletContextListener
examples:
log4j (javadoc):
LogManager.shutdown()
jdbc driver: (javadoc):
DriverManager.deregisterDriver(driver);
note: Also check for new versions of your 3rd party libs to check fox fixes concerning memory leaks (and/or thread local leaks).
Solution depends on the library that created these Threads/ThreadLocal-s.
Basically you need to call library's cleanup code from your CustomServletContextListener.contextDestroyed() method.
So find what is the library and how to shut it down properly.
You can try this code to remove all ThreadLocal
private void cleanThreadLocals() {
try {
// Get a reference to the thread locals table of the current thread
Thread thread = Thread.currentThread();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Object threadLocalTable = threadLocalsField.get(thread);
// Get a reference to the array holding the thread local variables inside the
// ThreadLocalMap of the current thread
Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapClass.getDeclaredField("table");
tableField.setAccessible(true);
Object table = tableField.get(threadLocalTable);
// The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
// is a reference to the actual ThreadLocal variable
Field referentField = Reference.class.getDeclaredField("referent");
referentField.setAccessible(true);
for (int i=0; i < Array.getLength(table); i++) {
// Each entry in the table array of ThreadLocalMap is an Entry object
// representing the thread local reference and its value
Object entry = Array.get(table, i);
if (entry != null) {
// Get a reference to the thread local object and remove it from the table
ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
threadLocal.remove();
}
}
} catch(Exception e) {
// We will tolerate an exception here and just log it
throw new IllegalStateException(e);
}
}
If we remove objects from thread local of all threads while stopping the container then we are only trying to solve the problem of error messages which are displayed by container. Rather, the objective should be to avoid memory leaks which may occurr over the period of time when container is not stopped/restarted. So, ideally as threads from ThreadPool are reused to serve different requests then after response is sent then there should not be any reason to hold memory by keeping objects in thread local because that thread may be used to serve next (totally different) request from client. One of the suggestion is to remove any objects from thread local by configuring Filter which is executed immediately before response is sent from server.
I would try to find which library causes these ThreadLocal, possibly by running the web app in a debugger and stopping on ThreadLocal creation. Then you can see if you forgot to clean up behind some library or if a library is buggy/not made for web app use. Maybe post your findings here.
When cleaning threads in a context listener, I once did check that the contextClassLoader of the thread was the same as the thread running the listener, to avoid messing the threads of other applications.
Hope this helps.
try
Runtime.getRuntime().addShutdownHook(webapp.new ShutdownHook());
in your shudownhook, clean the objects
If you use ThreadLocal in your code you could replace that ThreadLocal with ImrpovedThreadLocal that I made and you will not have a memory leak on stop/redeploy. You can use that threadLocal in the same way without having any thread contention.