Tuesday, June 09, 2009

Swing中JTextField的死锁问题

Swing中的JTextField如果使用不当,会造成死锁。这与JTextField中的两个锁有关系,一个是其继承来的Container.getTreeLock()方法返回的组件锁,一个是JTextField自己的readLock() writeLock()锁。

1)getTreeLock()会在每次计算组件大小(getPreferredSize方法)的时候调用。
2)设置JTextField的文字时则会在得到了writeLock后,再去尝试得到getTreeLock,已更新组件属性(本例中就是调用invalidate()方法)。
3)而一旦write锁不释放,所有尝试得到read锁的线程都将处于wait状态。
这三个条件就可以在多线程环境下构造出一个死锁。看下面的例程:

测试代码如下:
public class TryLock
{

/**
* @param args
*/
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.setSize(300, 300);
frame.setVisible(true);
final JTextField txt = new JTextField();
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(txt);
Thread th = new Thread(new Runnable()
{
public void run()
{
while (true)
{
try
{
Thread.currentThread().sleep(1);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
frame.pack();//1) pack时会调用JTextField的getPreferredSize方法,#必须得到read锁。
}
}
});
th.setName("循环pack");
th.start();

while (true)
{
try
{
Thread.currentThread().sleep(1);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable()
{

public void run()
{
txt.setText("asdfasdfasdf");// 2)必须得到write锁,并且在更新完毕后,释放write锁之前要getTreeLock()。
}

});

}
}
}

上面的例程一旦运行几乎马上就会陷入死锁状态。Swing线程处于等待状态,GUI不再repaint。

用了很久的Swing,总算知道啥叫Swing不是线程安全的了。状态不一致还好说,锁住事情可就大喽。

Sunday, June 07, 2009

Mark

我对于不够有意思的事情提不起长久的足够的精神头