线程与Java Swing
Java Swing非线程安全
多线程情况下访问Swing对象必须要遵守特殊的规则(保证只从单一线程来访问Swing对象)。
Swing程序会有多个线程,其中事件派发线程负责对所有Swing对象的访问和对象状态修改,保持所有对Swing对象的访问都被事件派发线程发出是排查多线程问题非常重要的关注点。
例外和规则
- 未呈现的Swing对象可被任何的thread创建与操作,但当Swing对象已经被呈现,则就只能通过事件派发线程来访问。
- Swing组件的repaint方法可以从任何的thread调用
- invokeLater()方法可以从任何thread调用
- invokeAndWait方法可以从任何不是事件派发的thread调用
示例_模拟处理耗时任务
package org.ybygjy.thread3th.demo2;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
/**
* 线程与Swing
* @author WangYanCheng
* @version 2012-10-31
*/
public class SwingThread extends JFrame implements Observer {
/**
* serial ID
*/
private static final long serialVersionUID = 6365731511993325046L;
/** 全局对象 */
private Map<String, JTextField> context;
/** 输入类组件ID前缀 */
private static String INPUT_FIELD_PREFIX = "INS_";
/** 输出类组件ID前缀 */
private static String OUTPUT_FIELD_PREFIX = "OUS_";
/**
* Constructor
*/
public SwingThread() {
context = new HashMap<String, JTextField>();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel ctxPanel = new JPanel();
ctxPanel.setBorder(new TitledBorder("计算斐波那契数:"));
System.out.println(getContentPane().getLayout());
GridLayout glInst = new GridLayout(10, 1);
ctxPanel.setLayout(glInst);
getContentPane().add(ctxPanel);
createTask(ctxPanel);
pack();
}
/**
* 创建任务
* @param mainPanel 主面板
*/
private void createTask(JPanel mainPanel) {
for (int i = 1; i <= 10; i++) {
JLabel tmplJLIn = new JLabel("输入:");
JLabel tmplJLOu = new JLabel("输出:");
JPanel jp = new JPanel();
jp.setBorder(new TitledBorder("第".concat(String.valueOf(i)).concat("组")));
jp.add(tmplJLIn);
JTextField jtf = new JTextField(5);
final String inputKey = INPUT_FIELD_PREFIX.concat(String.valueOf(i));
regInsComp(inputKey, jtf);
jp.add(jtf);
jp.add(tmplJLOu);
JTextField jtfo = new JTextField(5);
final String outputKey = OUTPUT_FIELD_PREFIX.concat(String.valueOf(i));
regInsComp(outputKey, jtfo);
jp.add(jtfo);
JButton jtnBtn = new JButton("计算");
jtnBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
FibBean fbInst = new FibBean();
fbInst.setInputKey(inputKey);
fbInst.setOutputKey(outputKey);
fbInst.setFibTerm(getInsValue(inputKey));
new FibonacciTask(SwingThread.this, fbInst);
}
});
jp.add(jtnBtn);
mainPanel.add(jp);
}
}
/**
* 注册组件
* @param key key
* @param jtf obj
*/
private void regInsComp(String key, JTextField jtf) {
context.put(key, jtf);
}
/**
* 取组件值
* @param key key
* @return rtnV 默认为0
*/
private long getInsValue(String key) {
return context.containsKey(key) ? Integer.parseInt(context.get(key).getText()) : 0;
}
/**
* 测试入口
* @param args 参数列表
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SwingThread().setVisible(true);
}
});
}
/**
* {@inheritDoc}
*/
public void update(Observable o, Object arg) {
if (arg instanceof FibBean) {
final FibBean fibBean = (FibBean) arg;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
context.get(fibBean.getOutputKey()).setText(String.valueOf(fibBean.getFibValue()));
}
});
}
}
}
package org.ybygjy.thread3th.demo2;
import java.util.Observable;
import java.util.Observer;
/**
* FibonacciTask
* @author WangYanCheng
* @version 2012-11-1
*/
public class FibonacciTask extends Observable implements Runnable {
/**{@link FibBean}*/
private FibBean fibBean;
/**
* Constructor
* @param observer {@link Observer}
* @param fibBean fibBean
*/
public FibonacciTask(Observer observer, FibBean fibBean) {
this.fibBean = fibBean;
addObserver(observer);
new Thread(this).start();
}
/**
* {@inheritDoc}
*/
public void run() {
this.fibBean.setFibValue(calc(this.fibBean.getFibTerm()));
setChanged();
notifyObservers(this.fibBean);
}
/**
* 计算逻辑(递归)
* @param fibNum 项数
* @return rtnV
*/
private long calc(long fibNum) {
if (fibNum <= 2) {
return 1;
}
return (calc(fibNum - 1) + calc(fibNum - 2));
}
}
package org.ybygjy.thread3th.demo2;
/**
* 数据Bean
* @author WangYanCheng
* @version 2012-11-1
*/
public class FibBean {
/**输入对象Key*/
private String inputKey;
/**输出对象Key*/
private String outputKey;
/**Fibonacci项*/
private long fibTerm;
/**Fibonacci值*/
private long fibValue;
/**
* @return the inputKey
*/
public String getInputKey() {
return inputKey;
}
/**
* @param inputKey the inputKey to set
*/
public void setInputKey(String inputKey) {
this.inputKey = inputKey;
}
/**
* @return the outputKey
*/
public String getOutputKey() {
return outputKey;
}
/**
* @param outputKey the outputKey to set
*/
public void setOutputKey(String outputKey) {
this.outputKey = outputKey;
}
/**
* @return the fibTerm
*/
public long getFibTerm() {
return fibTerm;
}
/**
* @param fibTerm the fibTerm to set
*/
public void setFibTerm(long fibTerm) {
this.fibTerm = fibTerm;
}
/**
* @return the fibValue
*/
public long getFibValue() {
return fibValue;
}
/**
* @param fibValue the fibValue to set
*/
public void setFibValue(long fibValue) {
this.fibValue = fibValue;
}
}