通常 Swing 不是线程安全的。除非另行说明,否则所有 Swing 组件及相关类都必须在事件调度线程上访问。
由于Swing不是线程安全的, 因此在实际操作过程中, 应避免通过多线程来操作UI. 在必要时, 应注意要将控件转移到事件调度线程。转移控件和开始处理 Swing 的首选方法是使用 invokeLater
。
简单的实践:
在如下小程序中, 通过最上方或最下方的Parse 都可以针对某文件或URL进行解析, 并解析出文件中含有的Email, 并归类但因在中间的TextArea中.
在按下Parse时, 将创建一个新的线程, 进行操作.
onParse: 为每个请求创建新的线程:
/** 响应Parse点击或textFilePath回车 - 为每个请求创建一个单独线程 */ private ActionListener onParse = new ActionListener() { public void actionPerformed(ActionEvent e){ //通过事件来源来判断读取哪个filePath; if(e.getSource().equals(buttonParse) || e.getSource().equals(textFilePath)) { filePathOnAction = textFilePath.getText(); }else if(e.getSource().equals(buttonParse2) || e.getSource().equals(textFilePath2)) { filePathOnAction = textFilePath2.getText(); } Thread threadParseEmail = new Thread(){ //将为每个请求创造一个线程 @Override public void run() { parseEmail(swui.filePathOnAction); } }; threadParseEmail.setName("ParseEmail-" + threadParseEmail.getName()); threadParseEmail.start(); log.debug("Parse clicked"); //使用log, 便于分辨线程 } }; //解析文件, 将ParseEmail返回信息打印到textOutPut中 private void parseEmail(final String filePath) { //buttonParse.setEnabled(false); log.info("正在解析"); labelInfoMessage.setText("正在解析"); if(labelImage.getIcon() == null) { labelImage.setIcon(imageParseing); }else { labelImage.setVisible(true); } //TODO Update UI code is put to the AWT-Event Queue so that only the AWT-Event Queue thread executes the UI update code. // Other threads may update UI directly but problems may arise in many cases. SwingUtilities.invokeLater(new Runnable() { @Override public void run() { log.info("Update UI code is put to the AWT-Event Queue so that only the AWT-Event Queue thread executes the UI update code"); try { if(filePath.startsWith("http")) { URL romatefile = new URL(filePath); textOutput.setText(pe.parseEmailAndReturnDomain(romatefile.openStream())); }else { textOutput.setText(pe.parseEmailAndReturnDomain(filePath)); } labelInfoMessage.setText("解析完毕"); } catch (Exception e) { log.debug("IO Exception - File does not exist"); labelInfoMessage.setText("文件不存在, 请确地址是否正确."); textOutput.setText("文件不存在, 请确地址是否正确."); } labelImage.setVisible(false); } }); }
log信息:
[AWT-EventQueue-0] DEBUG com.maill.parser.test.UI – Parse clicked //UI线程
[ParseEmail-Thread-2] INFO com.maill.parser.test.UI – 正在解析 //表明已经创建新的线程
[AWT-EventQueue-0] INFO com.maill.parser.test.UI – Update UI code is put to the AWT-Event Queue so that only the AWT-Event Queue thread executes the UI update code //将Thread-2中操作UI的部分交由UI线程处理.
=============================
类似的, 在SWT中, 不允许由非UI线程进行UI操作, 否则将会有Exception抛出. 而在Swing中, 不会抛出任何Exception, 但会产生不安全隐患.
SWT中的event loop flow:
上图中的Read adn dispatch:
//----------------------以下待删除-------------- // TODO 学习后删除; public static String getTestString() { StringBuilder sb = new StringBuilder();//新建一个SB sb.append("Print: "); for(int i=0; i < 10; i++) { if(i != 0) { sb.append(", "); // s += ", "; //BAD! } sb.append(i); // s += i; //BAD! } sb.append("a").append("b").append("C"). append("w"); return sb.toString(); System.out.println(sb.toString()); // return s; // BAD! // return "Id: " + id; // 简单情况下使用"+"链接, OK }
处理一个本地文件 或是一个InputStream
public String parseEmail(String fileName) throws IOException{ java.io.File file = new java.io.File(fileName); if(file.isFile()) { return parseEmail(new FileInputStream(fileName)); }else { throw new IOException(); } } public String parseEmail(InputStream in) throws IOException{ // InputStream in = new FileInputStream(fileName); InputStreamReader reader; reader = new InputStreamReader(in); BufferedReader br = new BufferedReader(reader); String data1; //按行读 while((data1= br.readLine()) != null) { System.out.println(data1); }
待续…
1. 编写并测试正则表达式.
使用工具:
2.
//定义正则表达式
//定义正则表达式 private static final String REGEX_EMAIL = "([\\w\\.-]{1,})@(([\\w-]{1,}\\.)+[a-zA-Z]{2,})"; //Group1, 2;
3. 使用
Pattern patternEmail = Pattern.compile(REGEX_EMAIL); java.util.regex.Matcher matcherEmail = patternEmail.matcher(data); while (matcherEmail.find()) { String pb = matcherEmail.group(1); String pa = matcherEmail.group(2).toLowerCase(); ,............ }
其中data为string类型
该正则表达式将取得data中所有的email地址.
其中group1为@前的字符 group2为@之后的domain.
API: http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/util/prefs/Preferences.html
需求: 某文件解析器, 设定解析的默认文件, 在关闭时将最后一次解析的路径保存, 再次打开时, 取出上次解析文件之路径.
用法:
取得针对此包的用户和系统参数选择的Preferences对象:
private Preferences userPrefs = Preferences.userNodeForPackage(SwingUI.class);
private Preferences sysPrefs = Preferences.systemNodeForPackage(SwingUI.class);
查询参数选择值[默认值]:
public void initPrefs() {
filePath = userPrefs.get("filePath", http://liguoliang.com/email.txt);
}
关闭之前保存最后一次解析文件的路径:
监听关闭事件:
/**//* 使用匿名类添加一个窗口监听器 */ addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { savePrefs(); System.out.println( "Exit when Closed event"); //退出应用程序 System.exit(0); } });
保存数据
public void savePrefs() { userPrefs.put("filePath", textFilePath.getText()); try { userPrefs.flush(); } catch (BackingStoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
如图: 第一次运行时, 默认路径: http://liguoliang.com/email.txt
在File中输入: http://xjst.org
点击关闭程序, 再次运行:
此时默认路径为上一次解析的http://xjst.org/
// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.