In a java application,when the user hits download,establishing the remote connection and downloading the content from remote is done in a separate thread and a dialog is popped up in the screen to show the download progress. Now a cancel command has been added to the dialog inorder to provide the user with an option of cancelling the download. When the user hits cancel button, dialog can be disposed using dispose() method from the program but how can I stop/kill the thread which has already been initiated? Thread does the following task: 1.Establishes connection with remote 2.Downloads content from remote 3.stores the content locally (content is transferred via streaming) Please help me to resolve this issue
You need to check for a stop flag somewhere in your download routine.
public DownloadThread implements Runnable {
private boolean stop;
public void stop() { stop = true; }
public void run() {
while (!stop) {
// download a block, save it somewhere
}
}
}
Of course this lacks the necessary synchronization but that’s about how you stop a thread without using the deprecated Thread.stop().
Firstly, the stop() operation on java.util.Thread is deprecated, and its use is strongly discouraged, since it can leave things in an unstable state. It's much preferred that you send a message to the Thread's Runnable asking it to stop itself safely.
The problem you have is that your thread is doing blocking I/O operations, and so it won't receive your message until the I/O is complete.
The best you can hope for, unless someone else comes up with a better option, is to interrupt() the Thread, and hope that the I/O classes notice and stop the download.
Edit: The javadoc for Thread.interrupt() does say that I/IO can be interrupted if you use java.nio, but it's very unlikely that you are. "Normal" java.io traffic is blocking, and cannot be interrupted.
Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?
Stopping a thread is probably the wrong way to look at. The actual resource consumed by a single thread on a desktop machine is irrelevant. Think of it as aborting the download.
If the read is blocking, then that isn't really much of a problem. You can wait until there is some data before not reading again. A more abrupt approach would be to call close
on the stream (from another thread).
I suggest you do what Bombe suggested (with a volatile variable) and just leave the thread to die in the background, returning control back to the user. It might spend a little while fetching the last block, but if the user can carry on doing something else it doesn't matter too much. If your block sizes were relatively small the wasted bandwidth wont be too much, and the IO stuff will timeout eventually and return if the connection has gone bad.
public class Downloader {
protected final AtomicBoolean run = new AtomicBoolean(false);
protected final byte[] file;
protected volatile double progress = 0.0;
public download(URL url) {
run.set(true);
new Thread() {
@Override
public run() {
final ByteBuffer buffer = new ByteBuffer();
while(run) {
/* download chunk, e.g add to buffer, or whatever */
buffer.put(chunk);
progress = buffer.size().size() / fileTotalSize; //e.g
}
syncrhonized(Downloader.this) {
file = buffer.array();
}
}
}.start();
}
public void abort() {
run.set(false);
}
public double getProgress() {
return progress;
}
public synchronized byte[] getFile() {
return file;
}
}