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.

Recommended Answers

All 6 Replies

Post the minimalistic code here which reproduces your problem instead of attaching a zip file.

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;
            }
        }
    }
}

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.

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

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.

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!

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.