熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

實戰體會Java多線程編程的精要

2013-11-23 19:52:35  來源: Java高級技術 

  在 Java 程序中使用多線程要比在 C 或 C++ 中容易得多這是因為 Java 編程語言提供了語言級的支持本文通過簡單的編程示例來說明 Java 程序中的多線程是多麼直觀讀完本文以後用戶應該能夠編寫簡單的多線程程序
  
  為什麼會排隊等待?
  下面的這個簡單的 Java 程序完成四項不相關的任務這樣的程序有單個控制線程控制在這四個任務之間線性地移動此外因為所需的資源 — 打印機磁盤數據庫和顯示屏 由於硬件和軟件的限制都有內在的潛伏時間所以每項任務都包含明顯的等待時間因此程序在訪問數據庫之前必須等待打印機完成打印文件的任務等等如果您正在等待程序的完成則這是對計算資源和您的時間的一種拙劣使用改進此程序的一種方法是使它成為多線程的
  
  四項不相關的任務
  
  class myclass {
  static public void main(String args[]) {
  print_a_file();
  manipulate_another_file();
  access_database();
  draw_picture_on_screen();
  }
  }
  在本例中每項任務在開始之前必須等待前一項任務完成即使所涉及的任務毫不相關也是這樣但是在現實生活中我們經常使用多線程模型我們在處理某些任務的同時也可以讓孩子配偶和父母完成別的任務例如我在寫信的同時可能打發我的兒子去郵局買郵票用軟件術語來說這稱為多個控制(或執行)線程
  
  可以用兩種不同的方法來獲得多個控制線程
  
  多個進程
  在大多數操作系統中都可以創建多個進程當一個程序啟動時它可以為即將開始的每項任務創建一個進程並允許它們同時運行當一個程序因等待網絡訪問或用戶輸入而被阻塞時另一個程序還可以運行這樣就增加了資源利用率但是按照這種方式創建每個進程要付出一定的代價設置一個進程要占用相當一部分處理器時間和內存資源而且大多數操作系統不允許進程訪問其他進程的內存空間因此進程間的通信很不方便並且也不會將它自己提供給容易的編程模型
  線程
  線程也稱為輕型進程 (LWP)因為線程只能在單個進程的作用域內活動所以創建線程比創建進程要廉價得多這樣因為線程允許協作和數據交換並且在計算資源方面非常廉價所以線程比進程更可取線程需要操作系統的支持因此不是所有的機器都提供線程Java 編程語言作為相當新的一種語言已將線程支持與語言本身合為一體這樣就對線程提供了強健的支持
  
  使用 Java 編程語言實現線程
  Java編程語言使多線程如此簡單有效以致於某些程序員說它實際上是自然的盡管在 Java 中使用線程比在其他語言中要容易得多仍然有一些概念需要掌握要記住的一件重要的事情是 main() 函數也是一個線程並可用來做有用的工作程序員只有在需要多個線程時才需要創建新的線程
  
  Thread 類
  Thread 類是一個具體的類即不是抽象類該類封裝了線程的行為要創建一個線程程序員必須創建一個從 Thread 類導出的新類程序員必須覆蓋 Thread 的 run() 函數來完成有用的工作用戶並不直接調用此函數而是必須調用 Thread 的 start() 函數該函數再調用 run()下面的代碼說明了它的用法
  
  創建兩個新線程
  
  import javautil*;
  
  class TimePrinter extends Thread {
  int pauseTime;
  String name;
  public TimePrinter(int x String n) {
  pauseTime = x;
  name = n;
  }
  
  public void run() {
  while(true) {
  try {
  Systemoutprintln(name + : + new
  Date(SystemcurrentTimeMillis()));
  Threadsleep(pauseTime);
  } catch(Exception e) {
  Systemoutprintln(e);
  }
  }
  }
  
  static public void main(String args[]) {
  TimePrinter tp = new TimePrinter( Fast Guy);
  tpstart();
  TimePrinter tp = new TimePrinter( Slow Guy);
  tpstart();
  
  }
  }
  在本例中我們可以看到一個簡單的程序它按兩個不同的時間間隔( 秒和 秒)在屏幕上顯示當前時間這是通過創建兩個新線程來完成的包括 main() 共三個線程但是因為有時要作為線程運行的類可能已經是某個類層次的一部分所以就不能再按這種機制創建線程雖然在同一個類中可以實現任意數量的接口但 Java 編程語言只允許一個類有一個父類同時某些程序員避免從 Thread 類導出因為它強加了類層次對於這種情況就要 runnable 接口
  
  Runnable 接口
  此接口只有一個函數run()此函數必須由實現了此接口的類實現但是就運行這個類而論其語義與前一個示例稍有不同我們可以用 runnable 接口改寫前一個示例(不同的部分用黑體表示
  
  創建兩個新線程而不強加類層次
  
  import javautil*;
  
  class TimePrinter implements Runnable {
  int pauseTime;
  String name;
  public TimePrinter(int x String n) {
  pauseTime = x;
  name = n;
  }
  
  public void run() {
  while(true) {
  try {
  Systemoutprintln(name + : + new
  Date(SystemcurrentTimeMillis()));
  Threadsleep(pauseTime);
  } catch(Exception e) {
  Systemoutprintln(e);
  }
  }
  }
  
  static public void main(String args[]) {
  Thread t = new Thread(new TimePrinter( Fast Guy));
  tstart();
  Thread t = new Thread(new TimePrinter( Slow Guy));
  tstart();
  
  }
  }
  請注意當使用 runnable 接口時您不能直接創建所需類的對象並運行它必須從 Thread 類的一個實例內部運行它許多程序員更喜歡 runnable 接口因為從 Thread 類繼承會強加類層次
  
  synchronized 關鍵字
  到目前為止我們看到的示例都只是以非常簡單的方式來利用線程只有最小的數據流而且不會出現兩個線程訪問同一個對象的情況但是在大多數有用的程序中線程之間通常有信息流試考慮一個金融應用程序它有一個 Account 對象如下例中所示
  
  一個銀行中的多項活動
  
  public class Account {
  String holderName;
  float amount;
  public Account(String name float amt) {
  holderName = name;
  amount = amt;
  }
  
  public void deposit(float amt) {
  amount += amt;
  }
  
  public void withdraw(float amt) {
  amount = amt;
  }
  
  public float checkBalance() {
  return amount;
  }
  }
  
  在此代碼樣例中潛伏著一個錯誤如果此類用於單線程應用程序不會有任何問題但是在多線程應用程序的情況中不同的線程就有可能同時訪問同一個 Account 對象比如說一個聯合帳戶的所有者在不同的 ATM 上同時進行訪問在這種情況下存入和支出就可能以這樣的方式發生一個事務被另一個事務覆蓋這種情況將是災難性的但是Java 編程語言提供了一種簡單的機制來防止發生這種覆蓋每個對象在運行時都有一個關聯的鎖這個鎖可通過為方法添加關鍵字 synchronized 來獲得這樣修訂過的 Account 對象(如下所示)將不會遭受像數據損壞這樣的錯誤
  
  對一個銀行中的多項活動進行同步處理
  
  public class Account {
  String holderName;
  float amount;
  public Account(String name float amt) {
  holderName = name;
  amount = amt;
  }
  
  public synchronized void deposit(float amt) {
  amount += amt;
  }
  
  public synchronized void withdraw(float amt) {
  amount = amt;
  }
  
  public float checkBalance() {
  return amount;
  }
  }
  
  deposit() 和 withdraw() 函數都需要這個鎖來進行操作所以當一個函數運行時另一個函數就被阻塞請注意 checkBalance() 未作更改它嚴格是一個讀函數因為 checkBalance() 未作同步處理所以任何其他方法都不會阻塞它它也不會阻塞任何其他方法不管那些方法是否進行了同步處理
  
  Java 編程語言中的高級多線程支持
  線程組
  線程是被個別創建的但可以將它們歸類到線程組中以便於調試和監視只能在創建線程的同時將它與一個線程組相關聯在使用大量線程的程序中使用線程組組織線程可能很有幫助可以將它們看作是計算機上的目錄和文件結構
  
  線程間發信
  當線程在繼續執行前需要等待一個條件時僅有 synchronized 關鍵字是不夠的雖然 synchronized 關鍵字阻止並發更新一個對象但它沒有實現線程間發信Object 類為此提供了三個函數wait()notify() 和 notifyAll()以全球氣候預測程序為例這些程序通過將地球分為許多單元在每個循環中每個單元的計算都是隔離進行的直到這些值趨於穩定然後相鄰單元之間就會交換一些數據所以從本質上講在每個循環中各個線程都必須等待所有線程完成各自的任務以後才能進入下一個循環這個模型稱為 屏蔽同步下例說明了這個模型
  <
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27599.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.