Java Forum / First Aid / September 2005
How do you _really_ get a PrintStream to flush all the time?
Rob McDonald - 29 Sep 2005 15:04 GMT I create my own PrintStream directed to a JTextArea to act as a console for a Swing application. I found an example of doing this online & implemented away...
Unfortunately, writing to the console isn't at all deterministic. Messages get trimmed. They are printed out of order, linefeeds don't happen. Generally, it is a mess. And no end of flush() does any good at all...
Any suggestions are appreciated,
Rob
public class Console extends JPanel { static PrintStream out; private PipedInputStream piOut; private PipedOutputStream poOut; private JTextArea textArea = new JTextArea();
public Console() throws IOException { piOut = new PipedInputStream(); poOut = new PipedOutputStream(piOut); out = new PrintStream(poOut, true); textArea.setEditable(false); textArea.setRows(10); textArea.setColumns(80); add(new JScrollPane(textArea), BorderLayout.CENTER); setVisible(true); new ReaderThread(piOut).start(); }
class ReaderThread extends Thread { PipedInputStream pi; ReaderThread(PipedInputStream pi) { this.pi = pi; } public void run() { final byte[] buf = new byte[1024]; try { while (true) { final int len = pi.read(buf); if (len == -1) { break; } SwingUtilities.invokeLater(new Runnable() { public void run() { textArea.append(new String(buf, 0, len)); // Make sure the last line is always visible textArea.setCaretPosition(textArea.getDocument().getLength()); // Keep the text area down to a certain character size int idealSize = 10000; int maxExcess = 5000; int excess = textArea.getDocument().getLength() - idealSize; if (excess >= maxExcess) { textArea.replaceRange("", 0, excess); } } }); } } catch (IOException e) { } } } }
Rob McDonald - 29 Sep 2005 15:54 GMT Ok, I just figured out a piece of the problem. The console is created with a reader thread. Additionally, each time something comes into the buffer, a new thread is created to append it to the JTextArea. Races between these threads are causing the problem.
If I remark out the subthread creation: SwingUtilities.invokeLater(new Runnable() { public void run() { and the appropriate corresponding closing brackets, behavior improves greatly.
However, then I run the danger of overrunning the buffer (and maybe other problems).
I suspect that some clever use of synchronize is the proper cure for my problem. I'm not sure whether to synchronize on the JTextArea or on the PrintStream, or what....
Thanks,
Rob
> I create my own PrintStream directed to a JTextArea to act as a console for > a Swing application. I found an example of doing this online & implemented [quoted text clipped - 59 lines] > } > } Roedy Green - 29 Sep 2005 17:14 GMT >Unfortunately, writing to the console isn't at all deterministic. Messages >get trimmed. They are printed out of order, linefeeds don't happen. >Generally, it is a mess. And no end of flush() does any good at all... See http://mindprod.com/applets/fileio.html
Pour out your sorrows to it, and it will show you how to use autoflush.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Rob McDonald - 29 Sep 2005 17:32 GMT > >Unfortunately, writing to the console isn't at all deterministic. Messages > >get trimmed. They are printed out of order, linefeeds don't happen. [quoted text clipped - 4 lines] > Pour out your sorrows to it, and it will show you how to use > autoflush. Sounds a bit like the Oracle. I'll give it a shot.
Rob
Rob McDonald - 29 Sep 2005 20:05 GMT > > See http://mindprod.com/applets/fileio.html > > > > Pour out your sorrows to it, and it will show you how to use > > autoflush. > > Sounds a bit like the Oracle. I'll give it a shot. Maybe I didn't pour enough of my sorrows into it (I have lots of them, it could take a while), but I honestly don't have the slightest idea how this is supposed to help me.
Rob
Oliver Wong - 29 Sep 2005 22:08 GMT > Maybe I didn't pour enough of my sorrows into it (I have lots of them, it > could take a while), but I honestly don't have the slightest idea how this > is supposed to help me. You're not alone; based on your description of the problem, it would seem to me that the source of all this confusion is your use of threads, and has nothing to do with FileIO.
The situation you've got is a classic problem in multithreading known as "producer/consumer". You should have a buffer of text (which is NOT the JTextArea). You've got a producer which will put text into the buffer. You should have a consumer which eats text out of the buffer and spits it back out into the JTextArea. You want the producer to not produce text so fast that it fills up the buffer and starts overwriting itself.
Try this tutorial: http://java.sun.com/docs/books/tutorial/essential/threads/synchronization.html
- Oliver
Roedy Green - 30 Sep 2005 06:59 GMT > You're not alone; based on your description of the problem, it would >seem to me that the source of all this confusion is your use of threads, and >has nothing to do with FileIO. That may well be. I was answering his question on how he could get an autoflush.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Rob McDonald - 30 Sep 2005 17:35 GMT > The situation you've got is a classic problem in multithreading known as > "producer/consumer". You should have a buffer of text (which is NOT the > JTextArea). You've got a producer which will put text into the buffer. You > should have a consumer which eats text out of the buffer and spits it back > out into the JTextArea. You want the producer to not produce text so fast > that it fills up the buffer and starts overwriting itself. As I mentioned, the original code was found on the web somewhere, and I must admit to pasting it in without taking the time to try to understand it.
In the producer/consumer model, I don't understand why the original author bothered with the second thread. From my point of view, the producers are the rest of your program. They throw text at the stream. The consumer thread reads from the stream and places the text in the JTextArea.
The original implementation's internal thread set just seems to insert a middleman, which increases the opportunities for race conditions and whatnot. (It probably could've been locked properly, but there was no synchronization done by that thread).
So, taking the code (clearing out any extraneous junk) and getting rid of the middleman gives this:
public class Console extends JPanel { static PrintStream out; private PipedInputStream piOut; private PipedOutputStream poOut; private JTextArea textArea = new JTextArea();
public Console() throws IOException { piOut = new PipedInputStream(); poOut = new PipedOutputStream(piOut); out = new PrintStream(poOut, true); add(new JScrollPane(textArea), BorderLayout.CENTER); setVisible(true); new ReaderThread(piOut).start(); }
class ReaderThread extends Thread { PipedInputStream pi; ReaderThread(PipedInputStream pi) { this.pi = pi; } public void run() { final byte[] buf = new byte[1024]; try { while (true) { final int len = pi.read(buf); if (len == -1) break; textArea.append(new String(buf, 0, len)); } } catch (IOException e) {} } } }
Because PipedInputStream / PipedOutputStream provide a buffered stream, the size of buf [1024] doesn't seem all that critical.
I realize that if multiple threads send data to the stream, the order they get displayed is unpredictable. However, PrintStream.println() is synchronized, so various messages shouldn't overlap or clash directly. Also, I think that if one thread sends lots of messages, it is theoretically possible for them to get displayed out of order.
I'm more concerned with preventing messages from getting truncated, lost, and corrupted. I'll deal with the rest of the realities of multi-threaded output.
Does anyone see a problem with the above code? Does anyone see a reason to have the middleman thread?
Rob
Roedy Green - 30 Sep 2005 05:45 GMT >Maybe I didn't pour enough of my sorrows into it (I have lots of them, it >could take a while), but I honestly don't have the slightest idea how this >is supposed to help me. Fid you tell it you wanted to write encoded characters to a file, say buffered, uncompressed. It would then have shown you the following code:
// Write Locale-encoded chars ( usually 8 bit ) into a buffered sequential file. // WARNING! unsigned Applets may not write to the local hard disk. // To copy/download files see http://mindprod.com/products.html#FILETRANSFER.
// import java.io.*;
// O P E N FileOutputStream fos = new FileOutputStream( "C:/temp/temp.out" ); OutputStreamWriter eosw = new OutputStreamWriter( fos,"UTF-8" ); // usually UTF-8 for Unicode 8-bit giving the full Unicode set, no BOMs. // Cp437 is IBM OEM. // See "encoding" in the Java glossary for alternatives // such as UTF-16BE for 16-bit, big-endian Unicode characters. BufferedWriter bw = new BufferedWriter( eosw, 4096 /* buffsize */ );
PrintWriter prw = new PrintWriter( bw, false /* auto flush on println */ ); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// W R I T E prw.write( "platypus" ); prw.println( 149 ); prw.flush();
// C L O S E prw.close();
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Roedy Green - 30 Sep 2005 05:43 GMT >Sounds a bit like the Oracle. I'll give it a shot. It is probably one of the ugliest programs ever written inside, but basically what I did was encapsulate as best I could every possible combination of source/destination, type of data, compressed/buffered and then glued bits of code together with a bit of logic so that it would produce a plausible snippet for any combination of i/o request.
There is so much asymmetry in the io classes!
But boy has it saved me a lot of typing. Any non-nio task I can just point people to the applet to cut and paste code that works to give them a skeleton starting point. I use it myself. It saves time because you don't have typos to clear out.
I have been thinking about writing a companion nio version. I am trying to think how such a mess could be better organised, perhaps using the enum feature.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Again taking new Java programming contracts.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|