My application freezes randomly with no error output. I've been beating my head over it's code and don't want to continue working on it until this problem is resolved. I'm thinking it's a synchronization problem. I've attached the source code. Any help would be greatly appreciated.
~s.o.s~ 2,560 Failure as a human Team Colleague Featured Poster
Post the minimalistic code here which reproduces your problem instead of attaching a zip file.
seanbp 4 Junior Poster
This is as small as I could get it. It happens when large amounts of text are managed.
public class JavaEdit extends JFrame {
public static void main(String[] args) {
new JavaEdit();
}
private JTextPane editText;
private StyledDocument editTextDoc;
private Style styleNormal;
private Style styleRED;
// Default constructor
private JavaEdit() {
new JavaEdit("one two three one two three one two three");
}
// Actual constructor
public JavaEdit(String string) {
this.setSize(800, 600);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
editText = new JTextPane();
editText.setText(string);
editTextDoc = (StyledDocument) editText.getDocument();
styleNormal = editTextDoc.addStyle("normal", null);
StyleConstants.setBackground(styleNormal, Color.black);
StyleConstants.setForeground(styleNormal, Color.white);
styleRED = editTextDoc.addStyle("normal", null);
StyleConstants.setBackground(styleRED, Color.black);
StyleConstants.setForeground(styleRED, Color.RED);
JScrollPane scroll = new JScrollPane(editText);
this.setLayout(new BorderLayout());
this.add(scroll, BorderLayout.CENTER);
new Thread(new Matcher()).start();
this.setVisible(true);
}
private class Matcher implements Runnable {
private Highlighter ohl;
private int ipos;
private int istart, stop;
private char cmatch;
private String Strcurrent;
class Painter extends DefaultHighlighter.DefaultHighlightPainter {
Painter(Color color) {
super(color);
}
}
Highlighter.HighlightPainter paint = new Painter(Color.MAGENTA);
public void run() {
while (true) {
try {
Thread.sleep(100);
search();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void search() throws Exception {
synchronized (editTextDoc) {
synchronized (editText) {
// clear old highlights and get cursor pos
ohl = editText.getHighlighter();
ohl.removeAllHighlights(); // todo: be specific
ipos = editText.getCaret().getDot();
}
// if on space move pos
if (editTextDoc.getText(ipos, 1).contains(" ")) {
ipos--;
}
// no action needed
if (editTextDoc.getLength() == 0) {
return;
}
// get pos of side ends of word
istart = stop = ipos;
cmatch = editTextDoc.getText(istart, 1).toCharArray()[0];
while (!isMatch(cmatch) && istart > 0) {
istart--;
cmatch = editTextDoc.getText(istart, 1).toCharArray()[0];
}
cmatch = editTextDoc.getText(stop, 1).toCharArray()[0];
while (!isMatch(cmatch) && stop < editTextDoc.getLength()) {
stop++;
cmatch = editTextDoc.getText(stop, 1).toCharArray()[0];
}
// get word
String word = editTextDoc.getText(istart, stop - istart).trim();
// no work done on whitespace
if (word.length() == 0) {
return;
}
// highlight matches
for (int i = 0; i < editTextDoc.getLength() - word.length() + 1; i++) {
Strcurrent = editTextDoc.getText(i, word.length());
if (word.contains(Strcurrent)) {
ohl.addHighlight(i, i + word.length(), paint);
}
}
}
}
boolean isMatch(char c) {
if (c == ' ') {
return true;
} else {
return false;
}
}
}
}
~s.o.s~ 2,560 Failure as a human Team Colleague Featured Poster
I've never worked on Swing/AWT but there are a couple of universal things you should know about single threaded UI toolkit (and Swing is one of them); all UI updates should be done on the rendering thread (EDT - Event dispatcher thread in case of Swing).
The problem with your code is that even though you have the motivation for running a background task, that task is very heavily involved with mutating the UI (getting the text from text area, finding highlights and highlighting them). And since you end up adding highlights in a background thread, it violates the principle of "UI updates only in the EDT. This is the reason you are getting a freeze or technically speaking a "deadlock". I'll also show you how to find out such deadlocks.
Assuming you are running JDK 6, it already comes bundled with a profiling tool called Visual VM in the bin directory. If not, just download it from the official site (google visual vm, it shouldn't be hard). Now, fire up your application and wait for it to freeze. After it has frozen/stopped responding, start Visual VM. After it has started, in the left panel of Visual VM under the "Local" category, you should see the classname of your application. Right click on it and click "Thread Dump". This should give you the thread dump of the currently executing threads. This feature also has the capability of detecting deadlocks. Just navigate to the bottom of that thread dump and search for the word "Found one Java-level deadlock:". Below that, you'll find the stack trace of two threads along with the explanation of why they have deadlocked. For me it gives (note I have renamed your class to Del):
Java stack information for the threads listed above:
===================================================
"Thread-2":
at javax.swing.text.DefaultHighlighter$SafeDamager.damageRange(DefaultHighlighter.java:591)
- waiting to lock <0x22e6e920> (a javax.swing.text.DefaultHighlighter$SafeDamager)
at javax.swing.text.DefaultHighlighter.safeDamageRange(DefaultHighlighter.java:287)
at javax.swing.text.DefaultHighlighter.safeDamageRange(DefaultHighlighter.java:296)
at javax.swing.text.DefaultHighlighter.addHighlight(DefaultHighlighter.java:107)
at home.projects.config.Del$Matcher.search(Del.java:131)
- locked <0x22e9e218> (a javax.swing.text.DefaultStyledDocument)
at home.projects.config.Del$Matcher.run(Del.java:86)
at java.lang.Thread.run(Thread.java:619)
"AWT-EventQueue-0":
at javax.swing.text.AbstractDocument.readLock(AbstractDocument.java:1366)
- waiting to lock <0x22e9e218> (a javax.swing.text.DefaultStyledDocument)
at javax.swing.plaf.basic.BasicTextUI.damageRange(BasicTextUI.java:1154)
at javax.swing.plaf.basic.BasicTextUI.damageRange(BasicTextUI.java:1137)
at javax.swing.text.DefaultHighlighter$SafeDamager.run(DefaultHighlighter.java:567)
- locked <0x22e6e920> (a javax.swing.text.DefaultHighlighter$SafeDamager)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Found 1 deadlock.
As you can clearly see from the explanation, for resources A and B, thread 1 has acquired A and is waiting for B whereas thread 2 has acquired B and is waiting for A resulting in a deadlock.
The solution? Re-write your code by reading some online tutorials which show you how you can actually run a UI mutating task in the background. A few tips:
- Instead of spawning off your UI application in the "main" thread (the code which you write in main()) consider using SwingUtilities.invokeLater() method
- Instead of busy waiting and peforming a task repeatedly, consider using event notifications i.e. run the "search" only if the "text" has changed
- I've heard that SwingWorker class is ideal for such tasks where updates of processing need to be reflected in the UI/interface
- Some mandatory reading: Doing swing right and How do I use SwingWorker
I'm sure others on this forum who have worked extensively on Swing might be able to add help you out further if you post an updated code based on the recommendations I've posted.
seanbp 4 Junior Poster
You've been a great help. I've resolved that issue; however there is a new issue arising from the instantiation of a new window thread. I'm afraid virtually every module so far is required to replicate this issue. I'll post it if someone asks. I've read both of the required readings to no avail. Here is my VisualVM Thread dump of all Threads not in a TIMED_WAIT or RUNNABLE state. Invalidation of my new window in a new thread is BLOCKED! I'm thinking something is static, but every threaded window locks up. Again, no exceptions are being thrown.
"Window #5" prio=10 tid=0x08b9e800 nid=0x3887 in Object.wait() [0xb4dd7000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x708b2870> (a javax.swing.text.DefaultStyledDocument)
at java.lang.Object.wait(Object.java:502)
at javax.swing.text.AbstractDocument.readLock(AbstractDocument.java:1389)
- locked <0x708b2870> (a javax.swing.text.DefaultStyledDocument)
at javax.swing.plaf.basic.BasicTextUI.getPreferredSize(BasicTextUI.java:913)
at javax.swing.JComponent.getPreferredSize(JComponent.java:1634)
at javax.swing.JEditorPane.getPreferredSize(JEditorPane.java:1389)
at javax.swing.ScrollPaneLayout.layoutContainer(ScrollPaneLayout.java:788)
at java.awt.Container.layout(Container.java:1481)
at java.awt.Container.doLayout(Container.java:1470)
at java.awt.Container.validateTree(Container.java:1568)
at java.awt.Container.validateTree(Container.java:1575)
at java.awt.Container.validateTree(Container.java:1575)
at java.awt.Container.validateTree(Container.java:1575)
at java.awt.Container.validateTree(Container.java:1575)
at java.awt.Container.validate(Container.java:1540)
- locked <0x7abe3cb8> (a java.awt.Component$AWTTreeLock)
at java.awt.Window.show(Window.java:861)
at java.awt.Component.show(Component.java:1468)
at java.awt.Component.setVisible(Component.java:1420)
at java.awt.Window.setVisible(Window.java:842)
at javaedit.JavaEdit.run(JavaEdit.java:153)
at java.lang.Thread.run(Thread.java:636)
Locked ownable synchronizers:
- None
"AWT-EventQueue-0" prio=10 tid=0x08c1e800 nid=0x3880 waiting for monitor entry [0xb4e2f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.awt.Component.invalidate(Component.java:2672)
- waiting to lock <0x7abe3cb8> (a java.awt.Component$AWTTreeLock)
at java.awt.Container.invalidate(Container.java:1506)
at javax.swing.JComponent.revalidate(JComponent.java:4791)
at javax.swing.plaf.basic.BasicTextUI$RootView.preferenceChanged(BasicTextUI.java:1411)
at javax.swing.text.View.preferenceChanged(View.java:289)
at javax.swing.text.BoxView.preferenceChanged(BoxView.java:286)
at javax.swing.text.View.preferenceChanged(View.java:289)
at javax.swing.text.BoxView.preferenceChanged(BoxView.java:286)
at javax.swing.text.View.preferenceChanged(View.java:289)
at javax.swing.text.View.preferenceChanged(View.java:289)
at javax.swing.text.GlyphView.changedUpdate(GlyphView.java:947)
at javax.swing.text.LabelView.changedUpdate(LabelView.java:303)
at javax.swing.text.View.forwardUpdateToView(View.java:1207)
at javax.swing.text.FlowView$LogicalView.forwardUpdateToView(FlowView.java:787)
at javax.swing.text.View.forwardUpdate(View.java:1178)
at javax.swing.text.View.changedUpdate(View.java:784)
at javax.swing.text.FlowView.changedUpdate(FlowView.java:283)
at javax.swing.text.ParagraphView.changedUpdate(ParagraphView.java:735)
at javax.swing.text.View.forwardUpdateToView(View.java:1207)
at javax.swing.text.View.forwardUpdate(View.java:1178)
at javax.swing.text.BoxView.forwardUpdate(BoxView.java:240)
at javax.swing.text.View.changedUpdate(View.java:784)
at javax.swing.plaf.basic.BasicTextUI$RootView.changedUpdate(BasicTextUI.java:1635)
at javax.swing.plaf.basic.BasicTextUI$UpdateHandler.changedUpdate(BasicTextUI.java:1896)
at javax.swing.text.AbstractDocument.fireChangedUpdate(AbstractDocument.java:231)
at javax.swing.text.DefaultStyledDocument$ChangeUpdateRunnable.run(DefaultStyledDocument.java:2595)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:226)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:602)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:138)
Locked ownable synchronizers:
- None
"AWT-Shutdown" prio=10 tid=0x08c1e000 nid=0x387f in Object.wait() [0xb4e80000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x7abd1b10> (a java.lang.Object)
at java.lang.Object.wait(Object.java:502)
at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:281)
- locked <0x7abd1b10> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:636)
Locked ownable synchronizers:
- None
~s.o.s~ 2,560 Failure as a human Team Colleague Featured Poster
I think you should post the latest updated code. That way, maybe I or someone else will give it a look and if it seems like a known issue, will let you know.
seanbp 4 Junior Poster
Here is my project so far. Work to be done. Also things are pretty nested. Feel free to do whatever you want with the provided files. I would also appreciate hints to better coding style if anyone has any advice. To replicate the error I type anything, start the server, then download a few times from localhost in the same app run, then boom!
The attachment preview is chopped off after the first 10 KB. Please download the entire file.
package javaedit;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
class Window {
// keep track of windows for closeWindow()
// todo: change to typed linked list
static Integer windowCount = 0;
}
public class JavaEdit extends JFrame implements Runnable {
public static void main(String[] args) {
new JavaEdit();
}
private String initialContent;
private Color initialColor;
private JTextPane editText;
private StyledDocument doc;
private JTextField address;
private JCheckBoxMenuItem serverCheckBox;
private JMenuItem connectOption;
private Thread serverThread = null;
private int port = 9101;
private JTextField portField;
private JFrame clientFrame;
// Default constructor
private JavaEdit() {
new JavaEdit("", Color.BLACK);
}
// Actual constructor
public JavaEdit(String StrinitContent, Color fontColor) {
initialContent = StrinitContent;
initialColor = fontColor;
synchronized (Window.windowCount) {
Window.windowCount += 1;
new Thread(this, "Window #"+Window.windowCount).start();
}
}
// every main window has one
public void run() {
this.setSize(800, 600);
this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
JMenu connections = new JMenu("Connections");
serverCheckBox = new JCheckBoxMenuItem("Serve Current Content");
serverCheckBox.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
invokeServer();
}
});
connectOption = new JMenuItem("Connect To...");
connectOption.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
connectDialog();
}
});
///
connections.add(serverCheckBox);
connections.add(connectOption);
menuBar.add(connections);
JMenu app = new JMenu("Application");
JMenuItem newOption = new JMenuItem("New Window");
newOption.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
new JavaEdit();
}
});
app.add(newOption);
JMenuItem closeOption = new JMenuItem("Close This Window");
closeOption.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
closeWindow();
}
});
app.add(closeOption);
JMenuItem closeall = new JMenuItem("Close All Windows");
closeall.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
app.add(closeall);
menuBar.add(app);
this.setJMenuBar(menuBar);
editText = new JTextPane();
editText.setEditable(true);
editText.setForeground(initialColor);
editText.setFont(Font.getFont("Monospaced"));
editText.setText(initialContent);
editText.addKeyListener(new Highlights());
editText.addCaretListener(new Highlights());
doc = (StyledDocument) editText.getDocument();
JScrollPane scroll = new JScrollPane(editText);
this.setLayout(new BorderLayout());
this.add(scroll, BorderLayout.CENTER);
this.setVisible(true);
}
private void closeWindow() {
synchronized (Window.windowCount) {
if (Window.windowCount == 1) {
System.exit(0);
}
if (serverThread != null) {
serverCheckBox.setState(false);
serverThread.interrupt();
serverCheckBox = null;
}
this.dispose();
Window.windowCount -= 1;
}
}
// This Dialog sets up, and selects the client connection type
private void connectDialog() {
// todo: move this method when if gets bloated
clientFrame = new JFrame();
clientFrame.setAlwaysOnTop(true);
clientFrame.setSize(300, 300);
clientFrame.setLayout(new FlowLayout());
address = new JTextField("localhost", 20);
clientFrame.add(address);
portField = new JTextField("9101", 20);
clientFrame.add(portField);
JButton download = new JButton("Download");
clientFrame.add(download);
download.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
clientConnection(1);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
JButton send = new JButton("Send");
clientFrame.add(send);
send.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
clientConnection(0);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
clientFrame.setVisible(true);
}
private void clientConnection(int mode) throws Exception {
// this is the client by which a server is accessed
// both send and receive
clientFrame.dispose();
// todo: replace ArrayList with typed linked list
port = Integer.parseInt(portField.getText());
String data = "";
Socket s = new Socket(address.getText(), port);
OutputStream os = new BufferedOutputStream(s.getOutputStream());
InputStream is = new BufferedInputStream(s.getInputStream());
os.write((byte) mode);
os.flush();
if (mode == 0) {
//send
synchronized (doc) {
data = doc.getText(0, doc.getLength());
}
char datachar[] = data.toCharArray();
for (char character : datachar) {
os.write((byte) character);
}
os.flush();
} else if (mode == 1) {
//download
byte buffer[] = new byte[1024];
int len = is.read(buffer);
while (len >= 0) {
data += new String(buffer, 0, len);
len = is.read(buffer);
}
new JavaEdit(data, Color.RED);
}
s.close();
}
private void invokeServer() {
try {
if (serverThread != null) {
serverThread.interrupt();
serverThread = null;
serverCheckBox.setState(false);
Thread.sleep(300);
return;
}
String SPort = JOptionPane.showInputDialog(this,
"Enter port number", "9101");
if (SPort == null) return;
JOptionPane.
showMessageDialog(this,
"Modifications will not be reflected on server until " +
"server is reset.");
port = Integer.parseInt(SPort); //todo: error checking
Server i = new Server(doc.getText(0, doc.getLength()), port);
serverThread = new Thread(i, "A server at port "+port);
serverThread.start();
serverCheckBox.setState(true);
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
class Highlights implements KeyListener, CaretListener {
// highlight all matches for string next to caret
private Highlighter ohl = null;
private ArrayList<Object> highlights = new ArrayList();
private int ipos;
private int istart, stop;
private char cmatch;
private String word;
private String Strcurrent;
private Highlighter.HighlightPainter paint =
new Painter(Color.LIGHT_GRAY);
public void keyTyped(KeyEvent e) {
doUpdate();
}
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void caretUpdate(CaretEvent e) {
doUpdate();
}
private void doUpdate() {
try {
// remove old highlights
ohl = editText.getHighlighter();
for (int i = highlights.size()-1; i >= 0; i--) {
ohl.removeHighlight(highlights.get(i));
highlights.remove(highlights.get(i));
}
// get caret ipos
ipos = editText.getCaret().getDot();
if (doc.getText(ipos, 1).contains(" ") ||
ipos == doc.getLength()) {
ipos--;
}
// no work for empty document
if (doc.getLength() == 0) {
return;
}
//get string to match with: word
istart = stop = ipos;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package javaedit;
import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
/**
*
* @author sean
*/
public class Server implements Runnable {
// this is the server by which sends and receives are hosted
// both send and receive
private String content;
private int port;
public Server(String content, int port) {
this.content = content;
this.port = port;
}
public void run() {
try {
ServerSocket ss = new ServerSocket(port);
ss.setSoTimeout(100);
while (true) {
nowServe(ss);
if (Thread.interrupted()) {
ss.close();
return;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
private void nowServe(ServerSocket ss) {
Socket s = null;
try {
try {
s = ss.accept();
} catch (SocketTimeoutException e) {
// not an error
//e.printStackTrace();
return;
}
InputStream is = new BufferedInputStream(s.getInputStream());
BufferedOutputStream os =
new BufferedOutputStream(s.getOutputStream());
int mode = is.read();
if (mode == -1) {
return;
}
if (mode == 0) {
//send at client
try {
byte[] buffer = new byte[1024];
int len = is.read(buffer);
String temp = "";
while (len >= 0) {
for (int i = 0; i < len; i++) {
temp += (char) buffer[i];
}
len = is.read(buffer);
}
new JavaEdit(temp, Color.WHITE);
} catch (Exception ex) {
ex.printStackTrace();
}
} else if (mode == 1) {
//download at client
char[] textchars = content.toCharArray();
for (int i = 0; i < content.length(); i++) {
os.write((byte) textchars[i]);
}
os.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
s.close();
} catch (Exception e) {
//e.printStackTrace();
return;
}
}
}
}
Edited by seanbp because: how to replicate error
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.