How can I timeout a function that blocks in Java?
Basically, I'm calling BufferedReader.ReadLine(); However I'm in a multithreaded server, where I'm synchronizing a node in a tree. So when this ReadLine function is called, if someone else reaches the node, they're locked out. I can't figure out how to make a timelimit on the amount of time ReadLine waits for a response before quitting the thread. The closest I got was to create a new thread that would sleep for 1ms then check to see if the variable that I set ReadLine to is changed. So something like this:
synchronized (pointer) {
String answer = "";
Thread d = new Thread(new Runnable() {
public void run() {
try {
int i = 0;
while (answer.equals("")) {
if (i == 10000) {
System.out.println("Timeout Occured");
System.exit(0);
}
try {
Thread.sleep(1);
i++;
}
catch(Exception e) {
System.out.println("sleep problem occured");
}
}
}
catch (IOException ex) {
}
}
});
d.start();
answer = socketIn.readLine();
}
This did what I wanted it to, but I couldn't figure out how to stop the current thread to unlock the node so other users could continue instead of killing the whole server. Finally, I thought maybe I could do this:
开发者_StackOverflow社区 Thread d = new Thread(new Runnable() {
public void run() {
try {
answer = socketIn.readLine();
} catch (IOException ex) {
}
}
});
d.join(10000);
catch (InterruptedException e){
socketOut.println("Timeout Occured. Returning you to the beginning...");
socketOut.flush();
return;
}
But this still seems to block and not be able to continue. Could someone help me out with this? I can't understand what I'm doing wrong?
I also tried to get the ExecutorService to work, but couldn't. Is this my answer? How would I implement it?
[EDIT] socketIn is a BufferedReader, should have said that explicitly sorry. Also, the client is connecting via telnet, though I don't think that matters.
What I'm doing here is a "celebrity guessing game" where users can add celebrities to the tree. So I need to lock the node that the person is editing for thread-safety
Is this homework? It's suspiciously close to a question asked by someone else yesterday. If so it should have the homework tag.
You only need to lock on something when a thread will modify data that other threads may read/modify.
If you're locking something waiting on input, the scope of your lock is far too broad.
Your flow should be:
- read input from client (blocking readLine())
- lock shared resource
- modify
- unlock
(This is assuming you've got one thread per connection/client, and are blocking on the read from the client)
That being said ... if you're reading from a socket and want it to time out, you need to use clientSocket.setSoTimeout(1000);
when you first accept the connection. If your BufferedReader
is waiting for that amount of time (in milliseconds) and doesn't get input, it will throw a java.net.SocketTimeoutException
String inputLine = null;
try
{
inputLine = in.readLine();
if (inputLine == null)
{
System.out.println("Client Disconnected!");
}
else
{
// I have input, do something with it
}
}
catch(java.net.SocketTimeoutException e)
{
System.out.println("Timed out trying to read from socket");
}
Everything is already done. Try to use java.util.concurrent
.
//
// 1. construct reading task
//
final FutureTask<String> readLine = new FutureTask<String> (
new Callable<String>() {
@Override public String call() throws Exception {
return socketIn.readLine();
}
}
);
//
// 2. wrap it with "timed logic"
// *** remember: you expose to your users only this task
//
final FutureTask<String> timedReadLine = new FutureTask<String> (
new Callable<String>() {
@Override public String call() throws Exception {
try {
//
// you give reading task a time budget:
// try to get a result for not more than 1 minute
//
return readLine.get( 1, TimeUnit.MINUTES );
} finally {
//
// regardless of the result you MUST interrupt readLine task
// otherwise it might run forever
// *** if it is already done nothing bad will happen
//
readLine.cancel( true );
}
}
}
)
{
//
// you may even protect this task from being accidentally interrupted by your users:
// in fact it is not their responsibility
//
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
};
Executor executor = Executors.newCachedThreadPool();
// 3. execute both
executor.execute( readLine );
executor.execute( timedReadLine );
// 4. ...and here comes one of your users who can wait for only a second
try {
String answer = timedReadLine.get(1, TimeUnit.SECONDS);
//
// finally user got his (her) answer
//
} catch (InterruptedException e) {
//
// someone interrupted this thread while it was blocked in timedReadLine.get(1, TimeUnit.SECONDS)
//
} catch (ExecutionException e) {
//
// e.getCause() probably is an instance of IOException due to I/O failure
//
} catch (TimeoutException e) {
//
// it wasn't possible to accomplish socketIn.readLine() in 1 second
//
}
I figured out a solution:
answer = "";
try{
Thread d = new Thread(new Runnable() {
public void run() {
try {
answer = socketIn.readLine();
}
catch (IOException ex) {
System.out.println("IO exception occurred");
}
}
});
d.join(10000); //Not sure if this is superfluous or not, but it didn't seem to work without it.
d.start();
i = 0;
while (true){
if (i == 10000){
if (d.isAlive()) throw InterruptedException;
}
if (answer.equals("")){
Thread.sleep(1);
}
else{
break;
}
i++;
}
//This essentially acts as Thread.sleep(10000), but the way I
//implemented it, it checks to see if answer is modified or not every
//.001 seconds. It will run for just over 10 seconds because of these checks.
//The number in Thread.sleep could probably be higher, as it is
//unnecessary to check so frequently and it will make the code more efficient
//Once it hits 10000, it throws an exception to move to the catch block below
//where the catch block returns everything to its original state and
//returns the client to the beginning
}
catch (Exception e){
socketOut.println("Timeout Occurred. Returning you to the beginning...");
socketOut.flush();
pointer = tree.root;
//reset class members to their original state
return;
}
Thanks for looking at it, Brian Roach. Your post was informative, but I accidentally omitted some key bits of information. I will try to be more careful in the future.
精彩评论