麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > Java > 正文

再論Java Swing線程

2019-11-17 06:34:22
字體:
來源:轉載
供稿:網友

  不正確的Swing線程是運行緩慢、無響應和不穩定的Swing應用的主要原因之一。這是許多原因造成的,從開發人員對Swing單線程模型的誤解,到保證正確的線程執行的困難。即使對Swing線程進行了很多努力,應用線程邏輯也是很難理解和維護的。本文闡述了如何在開發Swing應用中使用事件驅動編程,以大大簡化開發、維護,并提供高靈活性。

  背景

  既然我們是要簡化Swing應用的線程,首先讓我們來看看Swing線程是怎么工作的,為什么它是必須的。Swing API是圍繞單線程模型設計的。這意味著Swing組件必須總是通過同一個線程來修改和操縱。為什么采用單線程模型,這有很多原因,包括開發成本和同步Swing的復雜性--這都會造成一個遲鈍的API。為了達到單線程模型,有一個專門的線程用于和Swing組件交互。這個線程就是大家熟知的Swing線程,AWT(有時也發音為“ought”)線程,或者事件分派線程。在本文的下面的部分,我選用Swing線程的叫法。
既然Swing線程是和Swing組件進行交互的唯一的線程,它就被賦予了很多責任。所有的繪制和圖形,鼠標事件,組件事件,按鈕事件,和所有其它事件都發生在Swing線程。因為Swing線程的工作已經非常沉重了,當太多其它工作在Swing線程中進行處理時就會發生問題。會引起這個問題的最常見的位置是在非Swing處理的地方,像發生在一個事件監聽器方法中,比如JButton的ActionListener,的數據庫查找。既然ActionListener的actionPerformed()方法自動在Swing線程中執行,那么,數據庫查找也將在Swing線程中執行。這將占用了Swing的工作,阻止它處理它的其它任務--像繪制,響應鼠標移動,處理按鈕事件,和應用的縮放。用戶以為應用死掉了,但實際上并不是這樣。在適當的線程中執行代碼對確保系統正常地執行非常重要。

  既然我們已經看到了在適當的線程中執行Swing應用的代碼是多么重要,現在讓我們如何實現這些線程。我們看看將代碼放入和移出Swing線程的標準機制。在講述過程中,我將突出幾個和標準機制有關的問題和難點。正如我們看到的,大部分的問題都來自于企圖在異步的Swing線程模型上實現同步的代碼模型。從那兒,我們將看到如何修改我們的例子到事件驅動--移植整個方式到異步模型。
  通用Swing線程解決方案

  讓我們以一個最常用的Swing線程錯誤開始。我們將企圖使用標準的技術來修正這個問題。在這個過程中,我們將看到實現正確的Swing線程的復雜性和常見困難。并且,注重在修正這個Swing線程問題中,許多中間的例子也是不能工作的。在例子中,我在代碼失敗的地方以//broken開頭標出。好了,現在,讓我們進入我們的例子吧。

  假設我們在執行圖書查找。我們有一個簡單的用戶界面,包括一個查找文本域,一個查找按鈕,和一個輸出的文本區域。這個接口如圖1所示。不要批評我的UI設計,這個確實很丑陋,我承認。

再論Java Swing線程(圖一)
圖 1. 基本查詢用戶界面

  用戶輸入書的標題,作者或者其它條件,然后顯示一個結果的列表。下面的代碼例子演示了按鈕的ActionListener在同一個線程中調用lookup()方法。在這些例子中,我使用了thread.sleep()休眠5秒來作為一個占位的外部查找。線程休眠的結果等同于一個耗時5秒的同步的服務器調用。

PRivate void searchButton_actionPerformed()
{
 outputTA.setText("Searching for: " + searchTF.getText());
 //Broken!! Too mUCh work in the Swing
 thread String[] results = lookup(searchTF.getText());
 outputTA.setText("");
 for (int i = 0; i < results.length; i++)
 {
  String result = results[i];
  outputTA.setText(outputTA.getText() + '/n' + result);
  }
}
  假如你運行這段代碼(完整的代碼可以在這兒下載),你會立即發現存在一些問題。圖2顯示了查找運行中的一個屏幕截圖。

再論Java Swing線程(圖二)
圖 2. 在Swing線程中進行查找

  注重Go按鈕看起來是被按下了。這是因為actionPerformed方法通知了按鈕繪制為非按下外觀,但是還沒有返回。你也會發現要查找的字串“abcde”并沒有出現在文本區域中。searchButton_actionPerformed的第1行代碼將文本區域設置為要查找的字串。但是,注重Swing重畫并不是立即執行的。而是把重畫請求放置到Swing事件隊列中等待Swing線程處理。但是這兒,我們因查找處理占用了Swing線程,所以,它還不能馬上進行重畫。

  要修正這些問題,讓我們把查找操作移入非Swing線程中。我們第一個想到的就是讓整個方法在一個新的線程中執行。這樣作的問題是Swing組件,本例中的文本區域,只能從Swing線程中進行編輯。下面是修改后的searchButton_actionPerformed方法:

private void searchButton_actionPerformed()
{
 outputTA.setText("Searching for: " + searchTF.getText());
 //the String[][] is used to allow access to
 // setting the results from an inner class
 final String[][] results = new String[1][1];
 new Thread()
 {
  public void run()
  {
   results[0] = lookup(searchTF.getText());
   }
  }.start();
 outputTA.setText("");
 for (int i = 0; i < results[0].length; i++)
  {
  String result = results[0][i];
  outputTA.setText(outputTA.getText() + '/n' + result);
  }
}
  這種方法有很多問題。注重final String[][] 。這是一個處理匿名內部類和作用域的不得已的替代。基本上,在匿名內部類中使用的,但在外部環繞類作用域中定義的任何變量都需要定義為final。你可以通過創建一個數組來持有變量解決這個問題。這樣的話,你可以創建數組為final的,修改數組中的元素,而不是數組的引用自身。既然我們已經解決這個問題,讓我們進入真正的問題所在吧。圖3顯示了這段代碼運行時發生的情況:

再論Java Swing線程(圖三)
圖 3. 在Swing線程外部進行查找

  界面顯示了一個null,因為顯示代碼在查找代碼完成前被處理了。這是因為一旦新的線程啟動了,代碼塊繼續執行,而不是等待線程執行完畢。這是那些希奇的并發代碼塊中的一個,下面將把它編寫到一個方法中使其能夠真正執行。

  在SwingUtilities類中有兩個方法可以幫助我們解決這些問題:invokerLater()和invokeAndWait()。每一個方法都以一個Runnable作為參數,并在Swing線程中執行它。invokeAndWait()方法阻塞直到Runnnable執行完畢;invokeLater()異步地執行Runnable。invokeAndWait()一般不贊成使用,因為它可能導致嚴重的線程死鎖,對你的應用造成嚴重的破壞。所以,讓我們把它放置一邊,使用invokeLater()方法。
要修正最后一個變量變量scooping和執行順序的問題,我們必須將文本區域的getText()和setText()方法調用移入一個Runnable,只有在查詢結果返回后再執行它,并且在Swing線程中執行。我們可以這樣作,創建一個匿名Runnable傳遞給invokeLater(),包括在新線程的Runnable后的文本區域操作。這保證了Swing代碼不會在查找結束之前執行。下面是修正后的代碼:

private void searchButton_actionPerformed()
{
 outputTA.setText("Searching for: " + searchTF.getText());
 final String[][] results = new String[1][1];
 new Thread()
 {
  public void run()
  { //get results.
   results[0] = lookup(searchTF.getText())
   // send runnable to the Swing thread
   // the runnable is queued after the
   // results are returned
   SwingUtilities.invokeLater(
    new Runnable()
    {
     public void run()
     {
      // Now we're in the Swing thread
      outputTA.setText("");
      for (int i = 0; i < results[0].length; i++)
      {
       String result = results[0][i];
       outputTA.setText( outputTA.getText() + '/n' + result);
       }
      }
    }
   );
  }
 }.start();}
  這可以工作,但是這樣做令人非常頭痛。我們不得不對通過匿名線程執行的順序,我們還不得不處理困難的scooping問題。問題并不少見,并且,這只是一個非常簡單的例子,我們已經碰到了作用域,變量傳遞,和執行順序等一系列問題。相像一個更復雜的問題,包含了幾層嵌套,共享的引用和指定的執行順序。這種方法很快就失控了。



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 精品久久9999 | 91久久免费 | 亚洲第一色片 | 久久久电影电视剧免费看 | 欧美成在线视频 | 成人网在线观看 | 在线成人看片 | 美女性感毛片 | 最新91在线视频 | 欧美黄色看 | av成人免费看 | 俄罗斯hdxxx 日夜操天天干 | 久久国精品 | 毛片免费视频网站 | av不卡免费在线 | 神秘电影91 | av日韩一区二区 | www.com超碰 | 91短视频在线播放 | 成人午夜在线免费观看 | 亚洲aⅴ在线观看 | 电影av在线 | av在线免费看网站 | 国产成人av一区二区 | 日本网站在线播放 | 国产亚洲高清在线精品不卡 | 综合网日日天干夜夜久久 | 制服丝袜成人动漫 | 激情综合网俺也去 | 国产一国产精品一级毛片 | zzzzzzzxxxxxx日本人 | www.17c亚洲蜜桃 | 午夜视频免费播放 | 国产日韩一区二区三区在线观看 | 密室逃脱第一季免费观看完整在线 | 亚洲日本欧美 | 国产乱淫a∨片免费视频 | 日本高清一级片 | 国产亚洲精品久久久久5区 男人天堂免费 | 久久欧美亚洲另类专区91大神 | www.com香蕉 |