Stop/Kill Runnable Thread - Start new Runnable thread - Java - java

Im trying trying to stop a runnable thread from a Swing GUI. When I click on the button to stop the runnable thread it stops it but I am unable to start a new runnable thread afterwards.
Does anyone know why this is? Any help would be appreciated.
Thanks
Here's my GUI Code Listnere
if(button.getText().equals("Start Scraper")){
if(validate())
{
updateThread.running = true;
button.setText("Stop Scraper");
String searchType = comboBox.getSelectedItem().toString();
String email = emailTextField.getText();
String password = passwordTextField.getText();
String searchTerm = searchTermTextField.getText();
try{
thread = new updateThread(searchTerm, searchType, email, password );
thread.start();
}catch(Exception ex){
System.out.println("Something went wrong in the GUI");
ex.printStackTrace();
}
}else{
//not valid go again
}
}else{
button.setText("Start Crawler");
updateThread.running = false;
updateThread.terminate();
}
}
});
Here's my runnable thread class
package guiTool;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.SessionNotFoundException;
import linkedIncrawler.common.Utils;
import linkedin.actions.BaseClass;
import linkedin.actions.LinkedInActions;
public class updateThread extends Thread
{
private static WebDriver driver;
public String searchTerm, searchType, email, password;;
public volatile static Boolean running = true;
public updateThread(String searchTerm2, String searchType2, String email2, String password2)
{
email = email2;
password = password2;
searchTerm = searchTerm2;
searchType = searchType2;
}
public static void terminate() {
currentThread().interrupt();
//thread.stop();
driver.quit();
running = false;
}
#Override
public void run()
{
while(running)
{
try {
driver = Utils.OpenBrowser("SearchTerms");
new BaseClass(driver);
LinkedInActions.Execute(searchTerm, searchType, email, password);
driver.quit();
} catch (Exception e) {
System.out.println("2nd thread cant run linkedin");
e.printStackTrace();
}
}
}
}

Once a thread has died, it is dead... You need to create a new one. There are important reasons as to why you can't re-start a dead thread.
Rather than extending Thread, maybe implement Runnable/Callable?

public volatile static Boolean running = true;
this variable, being common for all instances, stops all updateThread's, current and future. Remove static modifier.

Related

Dispose Child Thread As per the Id

I am generating a child thread when I receive data from user.
What are the steps if I want to dispose the previous user child thread if the same user sends data again and wants to generate a new user child thread again?
Right, so java can't dispose of the thread, a thread simply runs until it terminates.
So:
To get rid of the thread you need to allow the threads run method to end and then get rid of all references to the Thread and any Runnable it's constructed with.
You want to toggle the thread finishing so, a simple example:
class SimpleRunnable implements Runnable {
public volatile boolean run = true; //Volatile for thread safety.
public void run() {
while(run) {
System.out.println("WHOOOO!"); //Boy, will this be annoying
}
}
}
Creating a thread from this runnable:
SimpleRunnable run = new SimpleRunnable();
Thread thread = new Thread(run);
Thread.start(); //run thread
//Stop thread
run.run=false;
//Thread will be removed when out of scope
Youu need to create a Runnable per user in your case, and then call set the stop variable when a new thread is created.
For example, you could store each runnable in a ConcurrentHashMap by userId.
ConcurrentHashMap<String,SimpleRunnable> runnablesByUser = new ConcurrentHashMap<>();
public void startNewThreadForUser(String userId){
//Time passes, retrieve and kill old thread:
SimpleRunnable oldRunnable = runnableByUser.get(userId);
if(oldRunnable!=null){
oldRunnable.run=false;
}
SimpleRunnable newRunnableUserOne = new SimpleRunnable();
runnablesByUser.put(userId,newRunnableUserOne);
Thread thread = new Thread(newRunnableUserOne);
thread.start();
}
Calls to the method would then kill an old thread if found, release the old one from scope by replacing it with a new one in the ConcurrentHashMap and finally start the new thread.
Like so:
public void startThreeThreads(){
startNewThreadForUser("User1");//starts Thread for User1
startNewThreadForUser("User2");//starts Thread for User2
startNewThreadForUser("User1");//Replaces Thread for User1
}
Managing running threads is typically done in a thread pool and this is rough in all sorts of ways, but hopefully it's useful.
I can elaborate that mechanism if you want.
Starting a new thread every time that you receive data from a user will lead to running out of resources, besides causing an unnecessary overhead of managing too many threads. Your computer has a limited number of threads that can run at any single time and is limited by your CPU. to find out that number you can use command
Runtime.getRuntime().availableProcessors()
on the other hand, if the jobs that you want to process require a lot of I/O processing, you should launch a few more threads than "Runtime.getRuntime().availableProcessors()", or you will be under-using your CPU.
what I would do is to use a "ExecutorService" which will handle the threads for you (no need to manually start, stop threads). Just start an "ExecutorService" with the total number of threads that you want to execute simultaneously, and then every time that you get more work from a User, submit the new task (as a Callable) to the ExecutorService. The executorService will handle the execution of that task for you, and once it is done it will become available for garbage collection.
for example, see code below:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MultipleClientsExample {
public static final int TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK = 4;
public static final Random random = new Random();
public static int customerCounter = 0;
public static void main(String[] args) throws InterruptedException {
MultipleClientsExample multipleClientsExample = new MultipleClientsExample();
multipleClientsExample.doTheWork();
}
private void doTheWork() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK);
while (customerCounter < 10) {
try {
CustomerInput customerInput = getWorkFromCustomer();
System.out.println("main program. received work from customer: " + customerInput.getCustomerId());
executorService.submit(new WorkToBeDone(customerInput.getCustomerId(), customerInput.getWorkInfo()));
} catch (InterruptedException e) {
break;
}
customerCounter++;
}
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
}
private CustomerInput getWorkFromCustomer() throws InterruptedException {
while (true) {
String customerId = String.valueOf(random.nextInt(10));
CustomerInput customerInput = new CustomerInput(customerId, "work from customer: " + customerId);
return customerInput;
}
}
}
class WorkToBeDone implements Callable<Void> {
private String clientId;
private String workInfo;
public WorkToBeDone(String clientId, String workInfo) {
this.clientId = clientId;
this.workInfo = workInfo;
}
#Override
public Void call() throws Exception {
System.out.println("inside a working thread: it is going to do the work of customer: " + clientId);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("worker processing job from customer: " + clientId + " was interrupted. ending now");
return null;
}
System.out.println("work completed for customer: " + clientId);
return null;
}
}
class CustomerInput {
private String customerId;
private String workInfo;
public CustomerInput(String customerId, String workInfo) {
this.customerId = customerId;
this.workInfo = workInfo;
}
public String getCustomerId() {
return customerId;
}
public String getWorkInfo() {
return workInfo;
}
}
In case you want the ability to cancel a task that has already been submitted to the thread pool, you will have to keep reference of the Future values of each task, and make sure to remove the reference of the tasks that completed and that you cancelled, so they are ready to be garbage collected (otherwise you will have a memory leak).
for example
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class MultipleClientsExample {
public static final int TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK = 4;
public static int customerCounter = 0;
public static void main(String[] args) throws InterruptedException {
MultipleClientsExample multipleClientsExample = new MultipleClientsExample();
multipleClientsExample.doTheWork();
}
private void doTheWork() throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(TOTAL_THREADS_TO_PROCESS_CUSTOMER_WORK);
Map<String, Future<String>> map = new ConcurrentHashMap<>();
while (customerCounter < 11) {
try {
WorkToBeDone workToBeDone = getWorkFromCustomer();
System.out.println("main program. received work from customer: " + workToBeDone.getClientId());
Future<String> resultFuture = executorService.submit(workToBeDone);
map.put(workToBeDone.getClientId(), resultFuture);
} catch (InterruptedException e) {
break;
}
customerCounter++;
}
// cancel job of customer with id: 10
Future<String> resultFuture = map.get("10");
System.out.println("cancelling job of customerId: 10");
resultFuture.cancel(true);
// remove references of all completed jobs
Thread.sleep(2000);
System.out.println("looking for jobs that completed or were cancelled.");
Iterator<Map.Entry<String, Future<String>>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Future<String>> entry = iterator.next();
if (entry.getValue().isCancelled() || entry.getValue().isDone()) {
System.out.println("removing reference of job for customer: " + entry.getKey());
iterator.remove();
}
}
// simpler way to remove entries from map (but doesn't print output of jobs removed from map)
// map.entrySet().removeIf(entry -> entry.getValue().isCancelled() || entry.getValue().isDone());
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);
}
private WorkToBeDone getWorkFromCustomer() throws InterruptedException {
String customerId = String.valueOf(customerCounter);
WorkToBeDone workToBeDone = new WorkToBeDone(customerId, "work from customer: " + customerId);
return workToBeDone;
}
}
class WorkToBeDone implements Callable<String> {
private String clientId;
private String workInfo;
public String getClientId() {
return clientId;
}
public WorkToBeDone(String clientId, String workInfo) {
this.clientId = clientId;
this.workInfo = workInfo;
}
#Override
public String call() throws Exception {
System.out.println("inside a working thread: it is going to do the work of customer: " + clientId);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("worker processing job from customer: " + clientId + " was interrupted. ending now");
return clientId;
}
System.out.println("work completed for customer: " + clientId);
return clientId;
}
}

How to wait until all threads complete their execution? [duplicate]

This question already has answers here:
wait until all threads finish their work in java
(17 answers)
Closed 6 years ago.
Say I'm using a HTTP requests library for downloading files. This library uses threads inside. Now, I want to wait on the main thread until other threads complete their execution.
All the other solutions that I found by googling only work if I have access to the Thread variables that were used in the library. But these are not accessible to me.
Here's what i'm using currently:
package smartzero.eightnoteight.testfirebase;
import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("email: ");
String email = in.nextLine();
System.out.print("password: ");
String password = in.nextLine();
Firebase fb = new Firebase("https://nullform.firebaseio.com");
fb.authWithPassword(email, password, new AuthResultHandler());
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class AuthResultHandler implements Firebase.AuthResultHandler {
#Override
public void onAuthenticated(AuthData authData) {
System.out.println("authentication successful");
String uid = authData.getUid();
new RunTests(uid);
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
System.out.println("authentication failed.");
}
}
}
PS: i'm testing firebase using firebase-client-jvm on my pc.
You should use the events provided by Firebase:
fb.authWithPassword(email, password, new AuthResultHandler(){
#Override
public void onAuthenticated(AuthData authData) {
//do something if authentication successful
}
#Override
public void onAuthenticationError(FirebaseError error) {
//handle error
}
});
You could, if you really want to wait in the main do this:
void main(String[] args) {
boolean finished = false;
fb.authWithPassword(email, password, new AuthResultHandler(){
#Override
public void onAuthenticated(AuthData authData) {
finished = true;
}
});
while (!finished){
Thread.sleep(1);
}
}
Thats more of a pseudocode. It doesnt catch the interrupted exception and blocks forever if there is an error (onAuthenticationError). Also i would not recommend this. Busy waiting is almost never a good idea.
On non-Android runtimes the Firebase Java client uses daemon threads, which will not prevent a process from exiting. You must handle this using a CountdownLatch or a Semaphore.
CountdownLatch
A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
Your code on it:
package smartzero.eightnoteight.testfirebase;
import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
Scanner in = new Scanner(System.in);
System.out.print("email: ");
String email = in.nextLine();
System.out.print("password: ");
String password = in.nextLine();
in.close();
Firebase fb = new Firebase("https://nullform.firebaseio.com");
CountDownLatch done = new CountDownLatch(1);
fb.authWithPassword(email, password, new Firebase.AuthResultHandler(){
#Override
public void onAuthenticated(AuthData authData) {
System.out.println("authentication successful");
done.countDown();
}
#Override
public void onAuthenticationError(FirebaseError error) {
System.out.println("authentication failed.");
done.countDown();
}
});
done.await();
}
}
Semaphore
It is used to control the number of concurrent threads that are using a resource. You could think of it as tickets to use a resource. You set the number of tickets available when you create it, and when acquire() is called with no tickets left, your process will wait for one to become available (on a release() call). On your code it is being created with zero "tickets" available:
package smartzero.eightnoteight.testfirebase;
import com.firebase.client.AuthData;
import com.firebase.client.Firebase;
import com.firebase.client.FirebaseError;
import java.util.Scanner;
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String[] args) throws InterruptedException {
Scanner in = new Scanner(System.in);
System.out.print("email: ");
String email = in.nextLine();
System.out.print("password: ");
String password = in.nextLine();
in.close();
Firebase fb = new Firebase("https://nullform.firebaseio.com");
Semaphore semaphore = new Semaphore(0);
fb.authWithPassword(email, password, new Firebase.AuthResultHandler(){
#Override
public void onAuthenticated(AuthData authData) {
System.out.println("authentication successful");
semaphore.release();
}
#Override
public void onAuthenticationError(FirebaseError error) {
System.out.println("authentication failed.");
semaphore.release();
}
});
semaphore.acquire();
}
}
A CountdownLatch is really good for having one thread wait for one or more threads to complete one or more tasks before proceeding.
First, create the countdown latch with count of n, where n is the number events you want to wait on. Next, give the latch to the thread or threads doing the work. After that, the thread that should wait calls await() on the latch, and simultaneously the other threads begin working. When each of the worker threads is done, they call countdown() on the latch. When the latch counter hits zero, the waiting thread (or possibly threads) will unblock.

I'm trying to run console input in a thread in Java but my program is getting hungup on the readLine() method

I'm creating this program to test getting user input in a thread for a chat server program. This program stops on read = into.readLine();. Why is that and what's happening?
Here is the code:
import java.io.*;
import java.net.*;
public class ThreadClass implements Runnable
{
DataInputStream in;
private boolean checkLoop = false;
public void run()
{
BufferedReader into = new BufferedReader(new InputStreamReader(System.in));
String read;
System.out.println("Welcome...");
while(!checkLoop)
{
try
{
System.out.println("running1");
read = into.readLine();
System.out.println(read);
if(read.equals(".bye"))
{
checkLoop = true;
}
Thread.sleep(500);
}
catch(IOException e)
{
System.out.println(e);System.out.println("running2");
}
catch (InterruptedException ie)
{
System.out.println(ie);System.out.println("running3");
}
}
System.out.println("running4");
}
public static void main(String[]args)
{
ThreadClass main = new ThreadClass();
Thread t1 = new Thread(main);
t1.start();
}
}
When you use "read = into.readLine();" the program will stop and wait for the user to press the "Enter" key. Your program is working fine from my point of view.
Try to type something in console and you will see the program running correctly.

java.awt.Robot thread can't be stopped/destroyed?

I made desktop remote control aplication using java.awt.Robot, and is working fine.
Application is integrated in another application system so I want to be able to start/stop remote control from outer system and I found strange problem - I can't stop Robot.
I tried several things: running it inside thread (so I can stop thread), making robot object null and than calling System.gc() but the problem isn't with reference it's robot thread that is still running (after all others are destroyed).
In debug I can see running thread : Daemon Thread [AWT-Windows] (Running).
Here is code that will reproduce my problem:
public class RobotDestroy {
public static void main(String[] args) {
try {
Robot r = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
}
}
Has anybody expirienced somthing similar ?
Any solutions ?
thanks
edit:
Here is example of running Robot in a thread that can be stopped but a Robot instance is still running:
public class RobotDestroy {
public static void main(String[] args) {
RobotThread rt = new RobotThread();
rt.start();
try {
//do some work before thread shut down
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
rt.shutdown();
}
private static class RobotThread extends Thread {
public Robot robot;
volatile boolean alive;
public RobotThread(){
try {
this.robot = new Robot();
this.alive = true;
} catch (AWTException e) {
e.printStackTrace();
}
}
public void run(){
while(alive){
System.out.println("alive");
try{
robot.delay(5000);
sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
public void shutdown(){
this.alive = false;
robot = null;
System.out.println("shutdown");
}
}
}
edit 2
I tried what doctor killer suggested and although it is a good suggestion Robot thread is still running. Best way to prove it is to print-out threads before creating robot instance and after:
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName());
}
UPDATE:
After a lot of debugging I realized that problem isn't in java.awt.Robot class - it is java.awt.Toolkit that starts AWT-Window thread that remains running after application ends.
Robot object has RobotPeer (java.awt.peer.RobotPeer) which is returned by : ((ComponentFactory)toolkit).createRobot(this, screen);
usually you would put a flag in your while(true) loop of the thread that receive the remote events so you would have something like while(isAlive) ... read remote event ... if remote event is disconnect you then get out of the loop and the robot would not get commands anymore.
The Robot is only still alive because your program keep calling it...your's to stop the loop
Also include this in your RobotThread
import java.awt.AWTException;
import java.awt.Robot;
import java.util.LinkedList;
public class RobotDestroy {
public static void main(String[] args) {
//Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]); for (int i = 0; i < threadArray.length; i++) { System.out.println("Init " + threadArray[i].getName()); }
RobotThread rt = new RobotThread();
rt.start();
// Listen to the upcomming commands...and push them to the RobotThread
rt.shutdown();
}
private static class RobotThread extends Thread {
public Robot robot;
public RobotThread(){
try {
this.robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
}
}
public void run(){
Command currentCommand = getNextCommand();
while(currentCommand.getType() != CommandType.KILL){
// Process the command no sleep...
// ...
// ...
currentCommand = getNextCommand();
}
System.out.println("DIED ");
}
private LinkedList<Command> commands = new LinkedList<Command>();
enum CommandType {
KILL,
DO_SOMETHING
}
private synchronized Command getNextCommand() {
while(commands.isEmpty()) {
try {
System.out.println("WAITING");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return commands.removeFirst();
}
public synchronized void pushCommand(Command comm) {
commands.addLast(comm);
notify();
}
public synchronized void shutdown(){
commands.clear();
pushCommand(new Command(CommandType.KILL));
System.out.println("shutdown");
//Set<Thread> threadSet = Thread.getAllStackTraces().keySet(); Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]); for (int i = 0; i < threadArray.length; i++) { System.out.println("End " + threadArray[i].getName()); }
}
class Command {
private CommandType cmdType;
public Command(CommandType type) {
this.cmdType = type;
}
public CommandType getType() { return cmdType; }
}
}
}

Implementing Wait and Notify

I'm trying to write a program that checks on the health of a database. One of the elements of the elements of the program is supposed to be that the program queries a database and then waits 5 minutes using wait. If there is no response it notifies and sends out some emails. My connection to the database/sending out emails all works, but i'm having trouble implementing wait and notify.
I read the api and its easy to understand in a simple program, but i'm really confused as to how to implement it in this case with all the additional complications because of errors where I can't call something dynamic from a static method.
I've been reading through lots of threads with wait and notify, but haven't figured out how do to it correctly in my program. If anyone could give me a few tips it would be a huge help. Thanks!
import com.fmr.ipgt.email.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import javax.mail.MessagingException;
class MyResource {
synchronized void qQuery() throws Exception {
String query = ".z.k"; // The query that is used to query q; this can be changed here.
int version = 0;
c qConn = null;
qConn = new c(Main.host,Main.port); // Connect to the q database
while (Main.healthy) {
Object o = qConn.k(query); // Query q
version = c.t(o);
if(!(version==0)) {
break; // End the process if the database responds
}
}
}
synchronized void start() throws Exception {
Main.setHealth(false);
Main.sendMessages();
}
}
class MyThread implements Runnable {
MyResource myResource;
MyThread(String name, MyResource so) {
myResource = so;
new Thread(this, name).start();
}
public void run() {
try {
myResource.qQuery(); // Begin a method to query q.
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Main {
private static String[] recipients;
private static String subject = "Database Failure";
private static String message = "The database has failed or is in a hung state";
private static String from;
static String host;
static int port;
private static String emails;
private static int minutes;
static boolean healthy = true;
public static void main(String args[]) throws Exception {
// Import information from the configuration file
SAXBuilder builder = new SAXBuilder();
File xmlFile = new File("/export/home/jflt/file.xml"); // Note: The directory for the configuration file may need to be changed
try {
Document document = (Document) builder.build(xmlFile);
Element rootNode = document.getRootElement();
List list = rootNode.getChildren("parameters");
Element node = (Element) list.get(0);
host = node.getChildText("host");
port = Integer.parseInt(node.getChildText("port"));
emails = node.getChildText("emails");
String delims = "[ ]+";
recipients = emails.split(delims); // parse email list
minutes = Integer.parseInt(node.getChildText("time"));
from = node.getChildText("from");
} catch (IOException io) {
System.out.println(io.getMessage());
} catch (JDOMException jdomex) {
System.out.println(jdomex.getMessage());
}
MyResource unhealthy = new MyResource();
new MyThread("MyThread", unhealthy); // Create new Thread
new MyThread("WaitThread", unhealthy);
while(healthy) {
Thread.sleep(minutes*60000); // The wrong thread is sleeping here. The main method should probably be a different thread instead which will then need to wait and the second thread will notify.
}
unhealthy.start(); // The database has not responded for the given time. Report that it is unhealthy.
}
public static void setHealth(boolean health){
System.out.println("database unhealthy");
healthy = health;
}
public static void sendMessages() throws MessagingException {
System.out.println("sending emails");
FCAPMailSender.postMail(recipients,subject,message,from);
}
}
If wait and notify is to be used, i will advice you to use the Lock Interface and Reentrant Lock Class from java.util.concurrent package...
Schedule a task with an ExecutorService that sends email. When you get a response, cancel the task. If it's been more than 5 minutes, the mail has already been sent by the executor thread, and cancellation is a no-op. Otherwise, the email is aborted.
This isn't even a wait()/notify() problem; there's no data passing between threads. Here's a low level equivalent to the ExecutorService solution.
void test() {
Thread t = new Thread() {
#Override
public void run() {
try { Thread.sleep(TimeUnit.MINUTES.toMillis(5)); }
catch(InterruptedException abort) { return; }
email();
}
}
t.start();
query();
t.interrupt();
}

Categories