aspose file tools*
The moose likes Swing / AWT / SWT and the fly likes Swing lag Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Swing lag" Watch "Swing lag" New topic
Author

Swing lag

Alex Birmingham
Ranch Hand

Joined: May 22, 2008
Posts: 54
I'm currently working on a project which includes as a front-end a swing interface. This interface consists of little more than a JTextPane and a with JScrollPane/Bar, and a JTextField: the former for output and the latter for input.

The major components of the program itself, aside from the front-end, include a thread pool, a bunch of tasks initiated by 'monster' objects which wait on a queue, and the client inputting commands to control his own 'player' object.

Currently I'm experiencing what I believe to be an inordinate amount of 'lag' or slowdown when several monsters and the player are all interacting with each other simultaneously. I believe this to be inordinate due to the fact that:
A) I am on a reasonably powerful computer(able to run Doom 3 with all the bells and whistles).
B) It's a text-based game, for crying out loud =)

I'm looking into the other aspects of my program, but I was wondering if anyone better acquainted with swing/awt could let me know what kind of overhead I'm using in regards to system resources with my 'Display' class, which acts as the front-end.

Thanks a ton, and here it is (abridged):

<blockquote>code:
<pre name="code" class="core">
// Creates and manages swing components
// Receives output information and displays it
// Can and should request input information via prompt(String query) method

package navea.system;

import java.util.*;
import navea.mobs.Player;
import navea.mobs.Mob;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;

public class Display implements ActionListener {

static Display display;
static JFrame frame;
static JTextPane textArea;
static StyledDocument styledDocument;
static JScrollPane scroller;
static JScrollBar verticalScroll;
static JTextField textField;
static Player player;

// The following boolean indicates whether or not client has entered information
static boolean actionPerformed = false;

// Initialization
public Display() {
frame = new JFrame();

textArea = new JTextPane();
Font font = new Font("Sans Serif", Font.PLAIN, 13);
textArea.setBackground(Color.BLACK);
Display.addStyle("ordinary", font, Color.getHSBColor(153, 153, 153));
textArea.setEditable(false);
styledDocument = textArea.getStyledDocument();
scroller = new JScrollPane(textArea);
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
verticalScroll = scroller.getVerticalScrollBar();

textField = new JTextField();
textField.setFont(font);
textField.addActionListener(this);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.CENTER, scroller);
frame.getContentPane().add(BorderLayout.SOUTH, textField);
frame.setSize(800, 750);
frame.setVisible(true); }

public static Display getInstance() {
if(display == null) { display = new Display(); } return display; }

public static void setPlayer(Player p) { player = p; }

// Output
// Output removed for ease of reading.

// ****Input****
// Where all the magic is supposed to happen.
// Sets actionPerformed to false, then just waits for the actionListener
// to indicate input has occured
public static ArrayList<String> prompt(String query) {
actionPerformed = false;
try { styledDocument.insertString(styledDocument.getLength(),
"\n " + query, styledDocument.getStyle("ordinary"));
} catch (Exception ex) { }
scrollToBottom();
while(actionPerformed == false) {
Thread.yield(); }
try { styledDocument.insertString(styledDocument.getLength(),
"\n " + textField.getText(), styledDocument.getStyle("ordinary"));
} catch (Exception ex) { }
String input[] = textField.getText().split(" ");
ArrayList<String> inputTokens = new ArrayList<String>();
for(String i: input) { inputTokens.add(i); }
for(int i = inputTokens.size(); i < 4; i++) {
inputTokens.add(" "); }
return inputTokens;
}

// Stylistic
public static void scrollToBottom() {
textArea.setCaretPosition(textArea.getDocument().getLength()); }

public static String capitalizeFirst(String text) {
String capitalizedStart = text.substring(0, 1).toUpperCase();
StringBuilder builder = new StringBuilder(text);
return builder.replace(0, 1, capitalizedStart).toString(); }

public static void addStyle(String name, Font font, Color color) {
Style style = textArea.addStyle("ordinary", null);
StyleConstants.setFontFamily(style, font.getFamily());
StyleConstants.setFontSize(style, font.getSize());
StyleConstants.setForeground(style, color);
StyleConstants.setBold(style, true); }

// TextField Listener
public void actionPerformed(ActionEvent av) {
actionPerformed = true;
try { styledDocument.insertString(styledDocument.getLength(),
"\n ", styledDocument.getStyle("ordinary"));
} catch (Exception ex) { }
textField.selectAll();
scrollToBottom(); }

}
</pre>
</blockquote>
[ July 15, 2008: Message edited by: Alex Birmingham ]

- SCJA 1.0
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24184
    
  34

This:

<blockquote>code:
<pre name="code" class="core">
while(actionPerformed == false) {
Thread.yield(); }
</pre>
</blockquote>

runs the CPU up to 100% while prompt() is executing, and furthermore, it's not guaranteed to work because the value of actionPerformed may be cached by a thread. You need to look into Java's mechanisms for communicating between threads.

Here's a great place to start.


[Jess in Action][AskingGoodQuestions]
Alex Birmingham
Ranch Hand

Joined: May 22, 2008
Posts: 54
Thanks a lot for the advice! Would you agree with this solution?

<blockquote>code:
<pre name="code" class="core"> // Prompt wait()'s for actionPerformed's notify(). wait() is encased within while(actionPerformed == false)
// so as to not break without 'notification' which is relevant. Both wait() and notify() are in synchronized
// blocks because otherwise an unwarrantedMonitor exception is thrown. (Though I don't entirely understand
// why....I mean, we're talking about one part of an instantiated singleton notifying another part of the
// same instantiated singleton. How would thread invoking part A not always be the same thread invoking part B?
// And thus part A thread obviously owns the lock to part B. Because it's the same lock!
// Is this a largely (in this particular case) symbolic action for the JVM's peace of mind?

public ArrayList<String> prompt(String query) {
actionPerformed = false;
try { styledDocument.insertString(styledDocument.getLength(),
"\n " + query, styledDocument.getStyle("ordinary"));
} catch (Exception ex) { }
scrollToBottom();
synchronized(this) {
while(actionPerformed == false) {
try { wait(); } catch (Exception ex) { } } } // Etc. etc. Do stuff with input....

public void actionPerformed(ActionEvent av) {
synchronized (this) {
notify(); }
actionPerformed = true;
</pre>
</blockquote>
[ July 15, 2008: Message edited by: Alex Birmingham ]
Ernest Friedman-Hill
author and iconoclast
Marshal

Joined: Jul 08, 2003
Posts: 24184
    
  34

Hi Alex,

Yep, that's exactly how it's done. Regarding the question in your comment: entering and exiting a synchronized block is compiled into special JVM instructions named "monitorenter" and "monitorexit". It is executing these instructions that ensures that the caches of the various threads are flushed to main memory appropriately. So grabbing the monitor, changing some variables, and then letting it go is essentially "publishing" those changes to other threads. If you make changes outside of a synchronized block, there's no guarantee that other threads will see the changes you've made. That's why it has to be done. wait() and notify(), which modify a state flag in the object that will be shared between threads, are merely enforcing correct practice.

A couple more things if I may, while you're listening: An empty "catch" block is a bad, bad, bad thing. If something goes wrong, you want to know about it; you don't want things to silently fail, leaving you to wonder "Hey, I called that method that does X, why didn't X happen?" Never write an empty catch block; always do something with the exception, even if it's just

<blockquote>code:
<pre name="code" class="core">
} catch (Exception ex) {
ex.printStackTrace();
}
</pre>
</blockquote>

Trust me: if you do not take this advice, the day will come, sooner rather than later, when you wish you had.

One more: don't say "while (x == true)"; say "while (x)". Similarly, don't say "while (x == false)"; say "while (!x)". It's shorter, clearer, and easier to get right.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Swing lag