隨筆-60  評論-35  文章-15  trackbacks-0
          一、簡介
          1 、什么是線程
            要說線程,就必須先說說進程,進程就是程序的運行時的一個實例。線程呢可以看作單獨地占有 CPU 時間來執(zhí)行相應的代碼的。對早期的計算機(如 DOS )而言,線程既是進程,進程既是進程,因為她是單線程的。當然一個程序可以是多線程的,多線程的各個線程看上去像是并行地獨自完成各自的工作,就像一臺一臺計算機上運行著多個處理機一樣。在多處理機計算機上實現(xiàn)多線程時,它們確實可以并行工作,而且采用適當?shù)姆謺r策略可以大大提高程序運行的效率。但是二者還是有較大的不同的,線程是共享地址空間的,也就是說多線程可以同時讀取相同的地址空間,并且利用這個空間進行交換數(shù)據(jù)。
          2 、為什么要使用線程
            為什么要使用多線程呢?學過《計算機體系結(jié)構(gòu)》的人都知道。將順序執(zhí)行程序和采用多線程并行執(zhí)行程序相比,效率是可以大大地提高的。比如,有五個線程 thread1, thread2, thread3, thread4, thread5 ,所耗的 CPU 時間分別為 4 5 1 2 7 。(假設 CPU 輪換周期為 4 CPU 時間,而且線程之間是彼此獨立的)順序執(zhí)行需要花費 19 CPU 時間,而并行需要的時間肯定少于 19 CPU 時間,至于具體多少時間要看那些線程是可以同時執(zhí)行的。這是在非常小規(guī)模的情況下,要是面對大規(guī)模的進程之間的交互的話,效率可以表現(xiàn)得更高。
          3 java 中是如何實現(xiàn)多線程的
            與其他語言不一樣的是,線程的觀念在 java 是語言中是重要的,根深蒂固的,因為在 java 語言中的線程系統(tǒng)是 java 語言自建的, java 中有專門的支持多線程的 API 庫,所以你可以以最快的速度寫一個支持線程的程序。在使用 java 創(chuàng)建線程的時候,你可以生成一個 Thread 類或者他的子類對象,并給這個對象發(fā)送 start() 消息(程序可以向任何一個派生自 Runnable 接口的類對象發(fā)送 start() 消息的),這樣一來程序會一直執(zhí)行,直到 run 返回為止,此時該線程就死掉了。
          java 語言中,線程有如下特點:
            § 在一個程序中而言,主線程的執(zhí)行位置就是 main 。而其他線程執(zhí)行的位置,程序員是可以自定義的。值得注意的是對 Applet 也是一樣。
            § 每個線程執(zhí)行其代碼的方式都是一次順序執(zhí)行的。

            § 一個線程執(zhí)行其代碼是與其他線程獨立開來的。如果諸線程之間又相互協(xié)作的話,就必須采用一定的交互機制。

            § 前面已經(jīng)說過,線程是共享地址空間的,如果控制不當,這里很有可能出現(xiàn)死鎖。
            各線程之間是相互獨立的,那么本地變量對一個線程而言就是完全獨立,私有的。所以呢,線程執(zhí)行時,每個線程都有各自的本地變量拷貝。對象變量 (instance variable) 在線程之間是可以共享的,這也就是為什么在 java 中共享數(shù)據(jù)對象是如此的好用,但是 java 線程不能夠武斷地訪問對象變量:他們是需要訪問數(shù)據(jù)對象的權(quán)限的。
          二、準備知識
            在分析這個例子之前,然我們先看看關(guān)于線程的幾個概念,上鎖,信號量,和 java 所提供的 API
          上鎖
            對于大多數(shù)的程序而言,他們都需要線程之間相互的通訊來完成整個線程的生命周期,二實現(xiàn)線程之間同步的最簡單的辦法就是上鎖。為了防止相互關(guān)聯(lián)的兩個線程之間錯誤地訪問共享資源,線程需要在訪問資源的時候上鎖和解鎖,對于鎖而言,有讀鎖,寫鎖和讀寫鎖等不同的同步策略。在 java 中,所有的對象都有鎖;線程只需要使用 synchronized 關(guān)鍵字就可以獲得鎖。在任一時刻對于給定的類的實例,方法或同步的代碼塊只能被一個線程執(zhí)行。這是因為代碼在執(zhí)行之前要求獲得對象的鎖。
          信號量
            通常情況下,多個線程所訪問為數(shù)不多的資源,那怎么控制呢?一個比較非常經(jīng)典而起非常簡單的辦法就是采用信號量機制。信號量機制的含義就是定義一個信號量,也就是說能夠提供的連接數(shù);當有一個線程占用了一個連接時,信號量就減一;當一個線程是放了連接時,信號量就加一。采用這種方法就可以簡單有效地控制線程的同步問題,而且實現(xiàn)起來也特別方便。看下面的代碼:
          class Semaphore {
          private int count;
          public Semaphore(int count) {
          this.count = count;
          }

          public synchronized void acquire() {
          while(count == 0) {
          try {
          wait();
          } catch (InterruptedException e) {
          //keep trying
          }
          }
          count--;
          }

          public synchronized void release() {
          count++;
          notify(); //alert a thread that
          s blocking on this semaphore
          }
          }
          java 中提供了哪些 api 以編寫多線程程序
            這里只列出幾個常用的方法和屬性值。
            屬性值,有三個 MAX_PRIORITY MIN_PRIORITY NORM_PRIORITY
            方法:
          Thread(); // 建立一個線程
          void run(); //
          對于一個繼承了 Runnable 接口的 class 而言,

          //
          他運行一個線程 , 否著他什么都不做

          void setPriority(int newPriority); //
          設置優(yōu)先級

          void start(); //
          運行一個程序

          void sleep(long millis); //
          線程睡眠 millis 毫秒

          static void yield(); //
          臨時 pause 一個程序以便起他線程運行
          三、程序示例
            例一、
            讓我們看看下面的例子。取錢的流程是輸入密碼,然后確定要取得金額,如果所取的金額小于或等于可以取出的金額, WITHDRAW 則返回 TRUE ,然后 ATM 機出錢,然后打印清單;否則返回 FALSE ,然后打印清單。如下圖:
          public class AutomatedTellerMachine extends Teller {
          public void withdraw(float amount) {
          Account a = getAccount();
          if (a.deduct(amount))
          dispense(amount);
          printReceipt();
          }
          }

          public class Account {
          private float total;
          public boolean deduct(float t) {
          if (t <= total) {
          total -= t;
          return true;
          }
          return false;
          }
          }
            就這個例子而言,假設有這種情況,對同一個賬號可以在不同的地方取錢,在同一時間,不同地點,妻子和丈夫取錢,妻子輸入了賬號上的最大金額,丈夫也是一樣,假如妻子輸入后已經(jīng)得到 true 的返回值,但是丈夫的線程所得到的值還沒有更新,這樣丈夫也能夠得到 true 的返回值,這樣就出現(xiàn)了問題!這個問題怎么解決呢?在 java 里面提供了控制機制以保證 deduct 操作時的原子性,那就是關(guān)鍵字 synchronized
            在 Account deduct 方法加入 synchronized 就可以解決這個問題。
            例二、
            在這里我們用多線程中最典型的例子,生產(chǎn)者與消費者問題。在這個例子里面我們定義了生產(chǎn)者 Producer ,消費者 Consumer 和倉庫 Warehouse 三個類,在整個程序的生命周期里,生產(chǎn)者隨機地制造出產(chǎn)品放到倉庫中,消費者也是隨即地從倉庫中取出產(chǎn)品。
          import exception.ProducerConsumerException;

          /**
          * Consumer.java
          * Consumer
          * By: Jiabo
          * Date: Mar 21, 2004
          * Time: 2:47:58 PM
          */
          public class Consumer extends Thread {

          private Warehouse warehouse;
          private String id;

          public Consumer(Warehouse warehouse, String id) {
          this.warehouse = warehouse;
          this.id = id;
          }

          public void run() {

          int tmp = (int) Math.random() * 10;

          try {
          warehouse.get(tmp);
          System.out.println("Consumer # " + this.id + " get " + tmp);
          } catch (ProducerConsumerException e) {
          e.printStackTrace();
          }

          try {
          sleep((int) (Math.random() * 100));
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          }
          }
            在這個類中,值得注意的一點是 run 方法中必須使用 try-catch ,因為,消費者從倉庫中取東西時有可能諸如倉庫中的儲量不夠得異常,在消費者里面也是一樣,只不過異常變?yōu)閭}庫已滿。
          import exception.*;

          /**
          * Producer.java
          * Producer
          * By: Jiabo
          * Date: Mar 21, 2004
          * Time: 2:47:45 PM
          */
          public class Producer extends Thread {

          private Warehouse warehouse;
          private String id;

          public Producer(Warehouse warehouse, String id) {
          this.warehouse = warehouse;
          this.id = id;
          }

          public void run() {

          int tmp = (int) Math.random() * 10;

          if (tmp != 0) {
          try {
          warehouse.put(tmp);
          System.out.println("Consumer # " + this.id + " put " + tmp);
          } catch (ProducerConsumerException e) {
          e.printStackTrace();
          }
          }

          try {
          sleep((int) (Math.random() * 100));
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          }
          }
            最重要的一部分在 Warehouse 類,如上所說為了保證 get set 的原子性,在這里使用了 synchronized 關(guān)鍵字,并且在操作時拋出了可能跑出的異常。
          import exception.*;

          /**
          * Warehouse
          * By: Jiabo
          * Date: Mar 21, 2004
          * Time: 2:48:10 PM
          */
          public class Warehouse {

          // max capability of the warehouse
          private int MAX;
          private int contents;

          // init with max capacity
          public Warehouse(int max) {
          this.MAX = max;
          this.contents = 0;
          }

          public synchronized void get(int amount) throws ProducerConsumerException {

          // the amount you want to get is bigger than the contends that the warehouse stores
          if (amount > this.contents) {
          throw new NotEnoughGoodsException();
          }

          amount -= contents;
          }

          public synchronized void put(int amount) throws ProducerConsumerException {

          // the amount you want to put is out of the capability of the warehouse
          if (amount > (this.MAX - this.contents)) {
          throw new WarehouseFullException();
          } else if (this.contents == 0) {
          // warehouse is empty
          throw new WarehouseEmptyException();
          }

          amount += contents;
          }
          }
          ?
          posted on 2006-05-08 23:58 Q系列類、方法、變量…… 閱讀(314) 評論(0)  編輯  收藏 所屬分類: 優(yōu)秀文章搜集
          主站蜘蛛池模板: 苏州市| 金沙县| 无棣县| 城固县| 迁安市| 衡阳市| 濉溪县| 荣昌县| 东辽县| 嘉兴市| 汕头市| 沅江市| 米林县| 商洛市| 中宁县| 小金县| 淅川县| 黎川县| 宁津县| 诏安县| 安新县| 城固县| 平原县| 彰化县| 霍城县| 梁山县| 临汾市| 咸阳市| 抚州市| 泽库县| 乃东县| 杨浦区| 永福县| 灵山县| 利辛县| 珠海市| 新疆| 德庆县| 阿拉善右旗| 周至县| 花垣县|