<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-124  評論-194  文章-0  trackbacks-0

    1、誰創建線程?
      即使您從未顯式地創建一個新線程,您仍可能會發現自己在使用線程。線程被從各種來源中引入到我們的程序中。
      有許多工具可以為您創建線程,如果要使用這些工具,應該了解線程如何交互,以及如何防止線程互相干擾。
      2、AWT 和 Swing
      任何使用 AWT 或 Swing 的程序都必須處理線程。AWT 工具箱創建單個線程,用于處理 UI 事件,任何由 AWT 事件調用的事件偵聽器都在 AWT 事件線程中執行。
      您不僅必須關心同步對事件偵聽器和其它線程之間共享的數據項的訪問,而且還必須找到一種方法,讓由事件偵聽器觸發的長時間運行任務(如在大文檔中檢查拼寫或在文件系統中搜索一個文件) 在后臺線程中運行,這樣當該任務運行時,UI 就不會停滯了(這可能還會阻止用戶取消操作)。這樣做的一個好的框架示例是 SwingWorker 類
      AWT 事件線程并不是守護程序線程;這就是通常使用 System.exit() 結束 AWT 和 Swing 應用程序的原因。
      3、使用 TimerTask
      JDK 1.3 中,TimerTask 工具被引入到 Java 語言。這個便利的工具讓您可以稍后在某個時間執行任務(例如,即從現在起十秒后運行一次任務),或者定期執行任務(即,每隔十秒運行任務)。
      實現 Timer 類非常簡單:它創建一個計時器線程,并且構建一個按執行時間排序的等待事件隊列。
      TimerTask 線程被標記成守護程序線程,這樣它就不會阻止程序退出。
      因為計時器事件是在計時器線程中執行,所以必須確保正確同步了針對計時器任務中使用的任何數據項的訪問。
      在 CalculatePrimes 示例中,并沒有讓主線程休眠,我們可以使用 TimerTask,方法如下:
       public static void main(String[] args) {
       Timer timer = new Timer();
       final CalculatePrimes calculator = new CalculatePrimes();
       calculator.start();
       timer.schedule(
       new TimerTask() {
       public void run()
       {
       calculator.finished = true;
       }
       }, TEN_SECONDS);
       }
      4、servlet 和 JavaServer Pages 技術
      servlet 容器創建多個線程,在這些線程中執行 servlet 請求。作為 servlet 編寫者,您不知道(也不應該知道)您的請求會在什么線程中執行;如果同時有多個對相同 URL 的請求入站,那么同一個 servlet 可能會同時在多個線程中是活動的。
      當編寫 servlet 或 JavaServer Pages (JSP) 文件時,必須始終假設可以在多個線程中并發地執行同一個 servlet 或 JSP 文件。必須適當同步 servlet 或 JSP 文件訪問的任何共享數據;這包括 servlet 對象本身的字段。
      5、實現 RMI 對象
      RMI 工具可以讓您調用對在其它 JVM 中運行的對象進行的操作。當調用遠程方法時,RMI 編譯器創建的 RMI 存根會打包方法參數,并通過網絡將它們發送到遠程系統,然后遠程系統會將它們解包并調用遠程方法。
      假設您創建了一個 RMI 對象,并將它注冊到 RMI 注冊表或者 Java 命名和目錄接口(Java Naming and Directory Interface (JNDI))名稱空間。當遠程客戶機調用其中的一個方法時,該方法會在什么線程中執行呢?
      實現 RMI 對象的常用方法是繼承 UnicastRemoteObject。在構造 UnicastRemoteObject 時,會初始化用于分派遠程方法調用的基礎結構。這包括用于接收遠程調用請求的套接字偵聽器,和一個或多個執行遠程請求的線程。
      所以,當接收到執行 RMI 方法的請求時,這些方法將在 RMI 管理的線程中執行。
      6、小結
      線程通過幾種機制進入 Java 程序。除了用 Thread 構造器中顯式創建線程之外,還可以用許多其它機制創建線程:
      AWT 和 Swing
      RMI
      java.util.TimerTask 工具
      servlet 和 JSP 技術

     

     

    共享變量

     

     

    1、 共享變量
      要使多個線程在一個程序中有用,它們必須有某種方法可以互相通信或共享它們的結果。
      讓線程共享其結果的最簡單方法是使用共享變量。它們還應該使用同步來確保值從一個線程正確傳播到另一個線程,以及防止當一個線程正在更新一些相關數據項時,另一個線程看到不一致的中間結果。
      線程基礎中計算素數的示例使用了一個共享布爾變量,用于表示指定的時間段已經過去了。這說明了在線程間共享數據最簡單的形式是:輪詢共享變量以查看另一個線程是否已經完成執行某項任務。
      2、存在于同一個內存空間中的所有線程
      正如前面討論過的,線程與進程有許多共同點,不同的是線程與同一進程中的其它線程共享相同的進程上下文,包括內存。這非常便利,但也有重大責任。只要訪問共享變量(靜態或實例字段),線程就可以方便地互相交換數據,但線程還必須確保它們以受控的方式訪問共享變量,以免它們互相干擾對方的更改。
      任何線程可以訪問所有其作用域內的變量,就象主線程可以訪問該變量一樣。素數示例使用了一個公用實例字段,叫做 finished,用于表示已經過了指定的時間。當計時器過期時,一個線程會寫這個字段;另一個線程會定期讀取這個字段,以檢查它是否應該停止。注:這個字段被聲明成 volatile,這對于這個程序的正確運行非常重要。在本章的后面,我們將看到原因。
      3、受控訪問的同步
      為了確保可以在線程之間以受控方式共享數據,Java 語言提供了兩個關鍵字:synchronized 和 volatile。
      Synchronized 有兩個重要含義:它確保了一次只有一個線程可以執行代碼的受保護部分(互斥,mutual exclusion 或者說 mutex),而且它確保了一個線程更改的數據對于其它線程是可見的(更改的可見性)。
      如果沒有同步,數據很容易就處于不一致狀態。例如,如果一個線程正在更新兩個相關值(比如,粒子的位置和速率),而另一個線程正在讀取這兩個值,有可能在第一個線程只寫了一個值,還沒有寫另一個值的時候,調度第二個線程運行,這樣它就會看到一個舊值和一個新值。同步讓我們可以定義必須原子地運行的代碼塊,這樣對于其他線程而言,它們要么都執行,要么都不執行。
      同步的原子執行或互斥方面類似于其它操作環境中的臨界段的概念。
      4、確保共享數據更改的可見性
      同步可以讓我們確保線程看到一致的內存視圖。
      處理器可以使用高速緩存加速對內存的訪問(或者編譯器可以將值存儲到寄存器中以便進行更快的訪問)。在一些多處理器體系結構上,如果在一個處理器的高速緩存中修改了內存位置,沒有必要讓其它處理器看到這一修改,直到刷新了寫入器的高速緩存并且使讀取器的高速緩存無效。
      這表示在這樣的系統上,對于同一變量,在兩個不同處理器上執行的兩個線程可能會看到兩個不同的值!這聽起來很嚇人,但它卻很常見。它只是表示在訪問其它線程使用或修改的數據時,必須遵循某些規則。
      Volatile 比同步更簡單,只適合于控制對基本變量(整數、布爾變量等)的單個實例的訪問。當一個變量被聲明成 volatile,任何對該變量的寫操作都會繞過高速緩存,直接寫入主內存,而任何對該變量的讀取也都繞過高速緩存,直接取自主內存。這表示所有線程在任何時候看到的 volatile 變量值都相同。
      如果沒有正確的同步,線程可能會看到舊的變量值,或者引起其它形式的數據損壞。
      5、用鎖保護的原子代碼塊
      Volatile 對于確保每個線程看到最新的變量值非常有用,但有時我們需要保護比較大的代碼片段,如涉及更新多個變量的片段。
      同步使用監控器(monitor)或鎖的概念,以協調對特定代碼塊的訪問。
      每個 Java 對象都有一個相關的鎖。同一時間只能有一個線程持有 Java 鎖。當線程進入 synchronized 代碼塊時,線程會阻塞并等待,直到鎖可用,當它可用時,就會獲得這個鎖,然后執行代碼塊。當控制退出受保護的代碼塊時,即到達了代碼塊末尾或者拋出了沒有在 synchronized 塊中捕獲的異常時,它就會釋放該鎖。
      這樣,每次只有一個線程可以執行受給定監控器保護的代碼塊。從其它線程的角度看,該代碼塊可以看作是原子的,它要么全部執行,要么根本不執行。
      6、簡單的同步示例
      使用 synchronized 塊可以讓您將一組相關更新作為一個集合來執行,而不必擔心其它線程中斷或看到計算的中間結果。以下示例代碼將打印“1 0”或“0 1”。如果沒有同步,它還會打印“1 1”(或“0 0”,隨便您信不信)。
      public class SyncExample {
       private static lockObject = new Object();
       private static class Thread1 extends Thread {
       public void run() {
       synchronized (lockObject) {
       x = y = 0;
       System.out.println(x);
       }
       }
       }
       private static class Thread2 extends Thread {
       public void run() {
       synchronized (lockObject) {
       x = y = 1;
       System.out.println(y);
       }
       }
       }
       public static void main(String[] args) {
       new Thread1().run();
       new Thread2().run();
       }
      }
      在這兩個線程中都必須使用同步,以便使這個程序正確工作。
      7、Java 鎖定
      Java 鎖定合并了一種互斥形式。每次只有一個線程可以持有鎖。鎖用于保護代碼塊或整個方法,必須記住是鎖的身份保護了代碼塊,而不是代碼塊本身,這一點很重要。一個鎖可以保護許多代碼塊或方法。
      反之,僅僅因為代碼塊由鎖保護并不表示兩個線程不能同時執行該代碼塊。它只表示如果兩個線程正在等待相同的鎖,則它們不能同時執行該代碼。
      在以下示例中,兩個線程可以同時不受限制地執行 setLastAccess() 中的 synchronized 塊,因為每個線程有一個不同的 thingie 值。因此,synchronized 代碼塊受到兩個正在執行的線程中不同鎖的保護。
      public class SyncExample {
       public static class Thingie {
       private Date lastAccess;
       public synchronized void setLastAccess(Date date) {
       this.lastAccess = date;
       }
       }
       public static class MyThread extends Thread {
       private Thingie thingie;
       public MyThread(Thingie thingie) {
       this.thingie = thingie;
       }
       public void run() {
       thingie.setLastAccess(new Date());
       }
       }
       public static void main() {
       Thingie thingie1 = new Thingie(),
       thingie2 = new Thingie();
       new MyThread(thingie1).start();
       new MyThread(thingie2).start();
       }
      }
      8、同步的方法
      創建 synchronized 塊的最簡單方法是將方法聲明成 synchronized。這表示在進入方法主體之前,調用者必須獲得鎖:
      public class Point {
       public synchronized void setXY(int x, int y) {
       this.x = x;
       this.y = y;
       }
      }
      對于普通的 synchronized方法,這個鎖是一個對象,將針對它調用方法。對于靜態 synchronized 方法,這個鎖是與 Class 對象相關的監控器,在該對象中聲明了方法。
      僅僅因為 setXY() 被聲明成 synchronized 并不表示兩個不同的線程不能同時執行 setXY(),只要它們調用不同的 Point 實例的 setXY() 就可同時執行。對于一個 Point 實例,一次只能有一個線程執行 setXY(),或 Point 的任何其它 synchronized 方法。
      9、同步的塊
      synchronized 塊的語法比 synchronized 方法稍微復雜一點,因為還需要顯式地指定鎖要保護哪個塊。Point 的以下版本等價于前一頁中顯示的版本:
      public class Point {
       public void setXY(int x, int y) {
       synchronized (this) {
       this.x = x;
       this.y = y;
       }
       }
      }
      使用 this 引用作為鎖很常見,但這并不是必需的。這表示該代碼塊將與這個類中的 synchronized 方法使用同一個鎖。
      由于同步防止了多個線程同時執行一個代碼塊,因此性能上就有問題,即使是在單處理器系統上。最好在盡可能最小的需要保護的代碼塊上使用同步。
      訪問局部(基于堆棧的)變量從來不需要受到保護,因為它們只能被自己所屬的線程訪問。
      10、大多數類并沒有同步
      因為同步會帶來小小的性能損失,大多數通用類,如 java.util 中的 Collection 類,不在內部使用同步。這表示在沒有附加同步的情況下,不能在多個線程中使用諸如 HashMap 這樣的類。

    posted on 2007-06-17 12:26 我愛佳娃 閱讀(682) 評論(0)  編輯  收藏 所屬分類: JAVA基礎
    主站蜘蛛池模板: 亚洲日本成本人观看| 日本一道本不卡免费| 在线观看免费人成视频色9| 午夜老司机免费视频| 亚洲国产成人久久77| 美女裸免费观看网站| 免费乱码中文字幕网站| 美女又黄又免费的视频| 亚洲免费视频一区二区三区| 青青免费在线视频| 久久久久亚洲AV成人网| 亚洲免费一区二区| 国产亚洲成av片在线观看| 亚洲xxxx18| 国产午夜无码片免费| 最近的中文字幕大全免费版| 在线综合亚洲欧洲综合网站| 日韩插啊免费视频在线观看| 亚洲成AV人综合在线观看| 永久免费的网站在线观看| 亚洲AⅤ男人的天堂在线观看| 24小时免费看片| 中文字幕乱码亚洲精品一区| 日本免费电影一区| 亚洲第一区二区快射影院| 日韩人妻无码免费视频一区二区三区 | 亚洲依依成人亚洲社区| 日本人成在线视频免费播放| 亚洲网红精品大秀在线观看| 黄页网址在线免费观看| 亚洲午夜久久久影院| 国产精彩免费视频| 亚洲精品天天影视综合网| 日本免费中文字幕| 亚洲欧美日韩综合久久久 | 国产自偷亚洲精品页65页| 黄色免费网址在线观看| 亚洲AV人无码综合在线观看| 永久免费AV无码国产网站| 一级特黄a免费大片| 亚洲福利视频网站|