0

I'm currently developing a Java command-line application and I want to make it interactive. I mean, I have 2 threads:

  • one thread is permanently writing some information to stdout
  • the second thread only reads from stdin and is waiting for some commands from the user

But the point is, it's really annoying when I'm typing a command while the first thread writes something to stdout, the command is "cut".

For example: I want to type the command 'set infolevel 2', and while I'm typing that, the first thread outputs "some information 1234".

My console output may look like this:

set inf                  // user typing command into stdin
some information 1234    // first thread writes out information
olevel 2                 // continuing reading command from stdin

It would be very awsome if someone could tell me if there's a library/API something like that. Thanks!

beeef
  • 2,664
  • 4
  • 17
  • 27
  • I think the way to do this is to read from stdin a character at a time (not sure exactly how to do), and when a new line comes in from the other thread, emit a character return, print the output, then a newline and the current line of characters. – Colonel Thirty Two Dec 07 '15 at 00:49
  • 1
    Are you asking about having two separate text areas, one for input, one for output? If so, have a look at http://stackoverflow.com/questions/12945537/how-to-set-output-stream-to-textarea about how to redirect stdout to a JTextArea. – Klitos Kyriacou Dec 07 '15 at 10:59
  • 1
    You could use the JCurses library to create separate windows within a console. http://sourceforge.net/projects/javacurses/ – Solomon Slow Dec 07 '15 at 13:40
  • Thank you, I'll have a look at this later .. – beeef Dec 07 '15 at 13:50

3 Answers3

1

I guess you would be using System.out.println for output to the console and new Scanner(System.in); for reading input.

Essentially System.out is a PrintStream and System.in is a InputStream and are different object (and streams) but they both use same source (I guess IDE console or command prompt in your case) so when 2 or more threads will try to use the same source then you will get the behavior you have mentioned because by default same source/console is used by the host environment or user. Read below from Java source:

System.out

/** * The "standard" input stream. This stream is already * open and ready to supply input data. Typically this stream * corresponds to keyboard input or another input source specified by * the host environment or user. */

System.in

/** * The "standard" output stream. This stream is already * open and ready to accept output data. Typically this stream * corresponds to display output or another output destination * specified by the host environment or user.


As far as I know, there are really no such API's to help you out what you are looking for but there are couple of work around options:

Please note, IMHO Option 3 or any similar solution which is based on synchronization is something which you may not want because it will limit your output'ing capabilities

Option 1:

Configure a different console device and then access it using System.console(). In this case, you will have different sources for read and write to console and hence you will not get what you are seeing. If you don't explicitly configure anything then by default you will get System.console() as NULL

Option 2:

Instead of writing the output to console, use a file to write the output. This will ensure that your input and output stream are not messing up with each other.

Option 3:

If you have same source for both read and write to console, then synchronize the access. Below is same code but please be mindful that you could still see some overlap for the time when object lock is being acquired and released but once lock is acquired or released on System.out or System.in then you will not see overlap.

public class Test {

    static Object object = new Object();

    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object) {
                        Date date = new Date();
                        System.out.println("I am coming from first thread i.e. " + Thread.currentThread().getId() + " : " + date);
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                while(true){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object) {
                        scanner.next();
                    }
                }
            }
        }.start();
    }

}
hagrawal7777
  • 14,103
  • 5
  • 40
  • 70
  • OK it sounds logical that when I use the same source for in and out that I get that behavior. But now I tried what you said at "Option 1", and I still have the same behavior .. – beeef Dec 07 '15 at 01:01
  • If your `System.console()` is still pointing to same source i.e. log viewer of IDE or command prompt then same behavior will occur. Here you need to configure (its possible but I also never tried so cannot tell you how) `System.console()` such that read it using IDE log viewer and writing is using command prompt. Basically what you need is different sources for read and write each. For me, option 2 is the most reasonable and rational. – hagrawal7777 Dec 07 '15 at 01:09
-1

It seems you need to synchronize the two threads on some kind of object. Something like : private Object lockObject = new Object(); Thread t1 = new Thread() { public void run() {synchronized( lockObject ){ //your code } } };

SomeDude
  • 13,876
  • 5
  • 21
  • 44
  • Why? That's not what I need. I just want some kind of "fixed" line for my input, no matter how much text is written to stdout. – beeef Dec 07 '15 at 00:00
  • My understanding is that while thread 1 is accepting user input, thread 2 is outputting some text to console. In that case I would halt thread 2 as long as thread 1 is listening for user input. In that case your code that reads input in thread 1 will lock the object, thread 2 will stop outputting. – SomeDude Dec 07 '15 at 00:08
  • @svasa Pretty sure the point is to be able for thread 2 to output stuff while the user is inputting a command. Locking the output stream would defeat the point. – Colonel Thirty Two Dec 07 '15 at 00:46
  • See, thread 2 is busy outputting some stuff, I don't want to mess that, and I am not locking output stream, I am locking the thread 2 until thread 1 reads in the user text on the console. If thread 2 is printing to console it will definitely stop printing user text at some point and then prints its own output. With that you will see breaks in user's text. That's exactly what the problem to be solved. – SomeDude Dec 07 '15 at 00:58
-1

You could either use locking as svasa suggested in his answer, or modify your code so that you have one thread that does both input and output, and the other thread does the computational work. Use an ArrayBlockingQueue to send the output of the work thread to the I/O thread.

Klitos Kyriacou
  • 10,634
  • 2
  • 38
  • 70