Suponía que habría alguna forma de hacerlo en java y suponía que de alguna forma utilizaría hilos.

Después de darle vueltas a un par de clases y tratar que un hilo desde el método run pudiermos lanzar una excepción resulta que hay otras maneras. Una de ellas, algo engorrosa utiliza clases del paquete concurrent de java. Es un especie de ejecutor de tareas al que le tenemos que pasar una clase que implemente un interfaz determinado. Luego lanzamos esa clase con un tiempo de timeout (en segundos o como queramos).

Si la tarea, que es asíncrona (en este caso está a la espera de lo que haga el usuario) no se termina, salta una excepción que desconocía que es TimeoutException.

Todo está basado en un post que encontré, pero en lugar de mostrarlo en partes, he metido todo en una clase para poder reutilizarlo en cualquier lado. En el código está la url de la solución que encontré, muy bien documentada.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;
import java.util.concurrent.

ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;



/**
 * @author Pello Xabier
 * As seen on
 * http://www.javaspecialists.eu/archive/Issue153.html
 * Simplificado y todo metido en una clase
 */
public class TimedInput {
        private long timeoutSecs;


        // Necesitamos una clase que implemente Callable que haga la tarea
        // De desta manera la podemos invocar desde un Executor
        private class ConsoleReadLineTask implements Callable<String> {
                /**
                 * método que debemos implementar para el interfaz Callable
                 * @return String
                 * @throws IOException
                 */
                  public String call() {
                    BufferedReader reader = new BufferedReader(new
InputStreamReader(System.in));

                    String readedInput = null;

                    try {
                        readedInput = reader.readLine();
                    } catch (IOException ioe) {

                    }
                    return readedInput;
                  }
        }


        /**
         * el constructor
         * @param timeoutSecs el número de segundos que esperaremos
         */
        public TimedInput (long timeoutSecs) {
                this.timeoutSecs = timeoutSecs;
        }


        /**
         * readLine
         * Este método es que el se invocará desde cualquier lado para recoger
         * información, y encapsula todo el tema del timeout
         * @param msg que se muestra al usuario para pedir datos
         * @return un String con lo que se ha escrito o null en caso de timeout.
         */
         public String readLine(String msg)  {
                    ExecutorService executorService = Executors.newSingleThreadExecutor();
                    String readedInput = null;
                    try {

                        // Future lo utilizamos para recoger el resultado
                        // de una tarea asíncrona, vamos, de la task que hemos preparado
                        Future<String> result = executorService.submit(new
ConsoleReadLineTask());

                        try {
                                System.out.println(msg);
                                // Aquí está la madre del cordero: espera el resultado un
determinado tiempo
                                // en caso de que no se haya completado la tarea SALTA UNA
EXCEPCIÓN DE TIMEOUT
                                // gracias a la cual sabemos que el usuario NO ha escrito nada.
                                readedInput = result.get(timeoutSecs, TimeUnit.SECONDS);
                        } catch (TimeoutException e) {
                          System.out.println("Timeout, no has escrito nada, dejamos
de esperar");
                          result.cancel(true);
                        } catch (Exception e) {
                          // capturamos cualquier cosa, for the sake of simplicity
                          // and the glory of your mother
                        }

                    } finally {
                        // esto detiene todas las tareas en ejecución y las que
estuvieran en espera
                        executorService.shutdownNow();
                    }
                    // Si salió bien devolveremos lo recogido en la Task que tiene el readLine
                    // y si no, va un null.
                    return readedInput;
                  }

}

En el algún foro también he visto alguna solución aún más simple.

http://www.coderanch.com/t/232213/threads/java/implement-timeout-threads