|
| |
Here's a program that breaks the Swing single thread rule:
package potentialCorruption;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
/**
* This class invokes multiple threads which (incorrectly) update the contents
* of a Swing component from outside the event thread.
*/
public class PotentialCorruptor extends JFrame
{
/**
* Main entry point for Java application
*/
public static void main(String[] args)
{
PotentialCorruptor frame = new PotentialCorruptor();
frame.setVisible(true);
}
/**
* Construct the class
*/
public PotentialCorruptor()
{
setTitle("Potential Corruptor Example");
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
Container panel = getContentPane();
panel.setLayout( new BorderLayout() );
m_buttonPanel = new ButtonPanel();
panel.add(m_buttonPanel, BorderLayout.CENTER);
m_statusLabel.setFont( new Font( "dialog", Font.BOLD, 14 ) );
panel.add(m_statusLabel, BorderLayout.SOUTH);
pack();
}
/**
* Button Panel
*/
class ButtonPanel extends JPanel
{
ButtonPanel()
{
setBackground(Color.blue);
m_startButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
setBackground(Color.red);
m_turnYellowButton.setEnabled(true);
m_textArea.setText("");
m_statusLabel.setText("Running...");
for (int i = 1; i < TOTAL_THREADS; i++)
{
Thread thread = new CorruptorThread("Thread " + i);
thread.start();
}
}
}
);
add(m_startButton);
m_turnYellowButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
ButtonPanel.this.setBackground(Color.yellow);
}
}
);
m_turnYellowButton.setEnabled(false); // Disable for now
add(m_turnYellowButton);
add( new JScrollPane(m_textArea) );
}
/**
* The thread to run
*/
class CorruptorThread extends Thread
{
CorruptorThread(String name)
{
super(name);
}
public void run()
{
int threadNumber = m_threadCount.incrementAndGet();
m_statusLabel.setText("Running (" + threadNumber + " threads)");
for (int i = 0; i < 50; i++)
{
try
{
Thread.sleep(1);
update("" + i);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}
update("Thread Done");
threadNumber = m_threadCount.decrementAndGet();
m_statusLabel.setText("Running (" + threadNumber + " threads)");
if (threadNumber == 0)
{
m_statusLabel.setText("Done!");
m_buttonPanel.setBackground(Color.blue);
m_turnYellowButton.setEnabled(false);
}
}
private void update(String text)
{
String temp = m_textArea.getText();
temp += "\n" + getName() + ": " + text;
m_textArea.setText(temp);
}
}
}
private ButtonPanel m_buttonPanel;
private JButton m_startButton = new JButton("Start (Turn Red)");
private JButton m_turnYellowButton = new JButton("Now Turn Yellow");
private JTextArea m_textArea = new JTextArea(10, 20);
private JLabel m_statusLabel = new JLabel("", SwingConstants.CENTER);
private static final int TOTAL_THREADS = 10;
private AtomicInteger m_threadCount = new AtomicInteger(0);
}
|
Note that the program fires up 9 threads, each of which repeatedly sleeps,
wakes up, updates the GUI (specifically a text area), and goes back to sleep
again, exiting after a relatively large number of iterations.
Note: I have specifically avoided
using the JTextArea's append() method, because, unlike most Swing methods,
it is thread-safe. Instead, I used a combination of
getText() and setText() methods to exhibit the behavior.
Try It!
Here is an applet that invokes the above code when you click on its
"Start" button:
|
|
After you've brought up the JFrame,
click on the "Now Turn Yellow"
button, and see what actually happens! |
Please be patient! This applet takes some time to complete.
Results
Did you notice that the contents of the JTextArea occasionally get
corrupted? Here's a small example I observed when I ran the program.
It's a snippet I copied and pasted from the JTextArea:
Thread 2: 13
Thread 7: 9
Thread 4: 12: 0
Thread 6: 0
Thread 2: 2
Even worse, you might experience more serious problems. I encountered
at least one occasion when the entire GUI locked up, and became totally
unresponsive. Probably, there was some internal corruption taking place.
|