Java第四部分

第十三章 多線程

  • 1,線程簡介

    • Java中的多線程在每個操作系統中的運行方式也存在差異,在此着重說明多線程在Windows操作系統中的運行模式。

    • Windows操作系統是多任務操作系統,它以進程爲單位。
      一個進程是一個包含有自身地址的程序,每個獨立執行的程序都稱爲進程,也就是正在執行的程序。

    • 系統可以分配給每個進程一段有限的使用CPU的時間(也稱爲CPU時間片),CPU在這段時間中執行某個進程,然後下一個時間片又跳至另一個進程中去執行。

    • 由於CPU轉換較快,所以使得每個進程好像是同時執行一樣。

    • 一個線程則是進程中的執行流程,一個進程中可以同時包括多個線程,每個線程也可以得到一小段程序執行時間,這樣一個進程就可以具有多個併發執行的線程。

    • 在單線程中,程序代碼按調用順序依次往下執行,如果需要一個進程同時完成多段代碼的操作,就需要產生多線程。

  • 2,線程的實現方式

    • 在Java中主要提供兩種方式實現線程:分別爲繼承java.lang.Thread類與實現java.lang.Runnable接口。

    • 繼承Thread類:Thread類是java.lang包中的一個類,從這個類中實例化的對象代表線程,程序員啓動一個新線程需要建立Thread實例。

      • Thread類中常用的構造方法:

        public Thread(String threadName);創建一個名稱爲threadName的線程對象。
        public Thread();
        
        • 完成線程真正功能的代碼放在類的Run()方法中,當一個類繼承Thread類後,就可以在該類中覆蓋run()方法,將實現該線程功能的代碼寫入run()方法中,

        • 然後同時調用Thread類中的start()方法執行線程,也就是調用run()方法。

        • run()方法的格式必須是:public void run(){}

      • 當執行一個線程程序時,就自動產生一個線程,主方法正是在這個線程上運行的。當不再啓動其他線程時,該程序就爲單線程程序。

      • 主方法線程啓動由Java虛擬機負責,程序員負責啓動自己的線程。

      • 通常在run()方法中使用無限循環的形式,使得線程一直運行下去,所以要指定一個跳出循環的條件。

      • 在main方法中,使線程執行需要調用Thread類中的start()方法,start()方法調用被覆蓋的run()方法,如果不調用start()方法,線程永遠都不會啓動,在主方法沒有調用start()方法之前,Thread對象只是一個實例,而不是一個正真的線程。

    • 實現Runable接口:如果程序員需要繼承其他類(非Thread類),而且還要使當前類實現多線程,那麼可以通過Runable接口來實現。

      • 實質上Thread類實現了Runnable接口,其中的run()方法正是對Runnable接口中的run()方法的具體實現。

        • Thread類中有以下兩個構造方法:

          public Thread(Runnable r);
          public Thread(Runnable r,String name);
          

          使用以上構造方法就可以將Runnable實例與Thread實例相關聯。

        • 使用Runnable接口啓動新線程的步驟如下:

          • (1)建立Runnable對象。
          • (2)使用參數爲Runnable對象的構造方法創建Thread實例。
          • (3)調用start()方法啓動線程。
        • 通過Runnable接口創建線程時程序員首先需要編寫一個實現Runnable接口的類,然後實例化該類的對象,這樣就建立Runnable接口的類;

        • 接下來使用相應的構造方法創建Thread實例;最後使用該實例調用Thread類中的Start()方法啓動線程。

  • 3,線程的生命週期

    • 線程具有生命週期,其中包含7中狀態,分別爲出生狀態、就緒狀態、運行狀態、等待狀態、休眠狀態和死亡狀態。

    • 出生狀態就是線程被創建時處於的狀態,在用戶使用線程實例調用start()方法之前線程都處於出生狀態;

    • 當用戶調用start()方法後,線程處於就緒狀態(又被稱爲可執行狀態);

    • 當線程得到系統資源後就進入運行狀態;

    • 一旦線程進入可執行狀態,它會在就緒與運行狀態下轉換,同時也有可能進入等待、休眠、阻塞狀態或死亡狀態;

    • 當處於運行狀態下的線程調用Thread類中的wait()方法時,該線程便進入等待狀態,進入等待狀態的線程必須調用Thread類中的notify()方法才能被喚醒,而notifyAll()方法是將所有處於等待狀態下的線程喚醒;

    • 當線程調用Thread類中的sleep()方法時,則會進入休眠狀態。
      如果一個線程在運行狀態下發出輸入/輸出請求,該線程將進入阻塞狀態,在其等待輸入/輸出結束時線程進入就緒狀態;

    • 對於阻塞的線程來說,即使系統資源空閒,線程依然不能回到運行狀態;

    • 當線程的run()方法執行完畢時,線程進入死亡狀態。

    • 使線程處於就緒狀態有以下幾種方法:

      • 調用sleep()方法;
      • 調用wait()方法;
      • 等待輸入/輸出完成;
    • 當線程處於就緒狀態後,可以使用以下幾種方法使線程再次進入運行狀態:

      • 線程調用notify()方法;
      • 線程調用notifyAll()方法;
      • 線程調用interrupt()方法;?
      • 輸入/輸出結束。
  • 4,操作線程的方法

    • 線程的休眠:一種能控制線程行爲的方法是調用sleep()方法,sleep()方法需要一個參數用於指定該線程休眠的時間,該時間以毫秒爲單位。

    • 線程的加入:如果當前某程序爲多線程程序,假如存在一個線程A,現在需要插入線程B,並且要求線程B先執行完畢,然後再繼續執行線程A,此時可以使用Thread類中的join()方法來完成。

    • 線程的中斷:以前使用stop()方法停止線程,現已廢除。現在提倡在run()方法中使用無限循環的形式,然後使用一個布爾型標記控制循環的停止。

    • 線程的優先級:每個線程都有自己的優先級,線程的優先級可以表明在程序中該線程的重要性,

      • 如果有很多線程處於就緒狀態,系統會根據優先級來決定首先使哪個線程進入運行狀態。但這並不意味着低優先級的線程得不到運行,而只是它們運行的機率比較小,如垃圾回收線程的優先級就較低。

      • Thread類中包含的成員變量代表了線程的某些優先級,如Thread.MIN_PRIORITY(常數1)、Thread.MAX_PRIORITY(常數10)、Thread.NORM_PRIORITY(常數5)。

      • 在默認情況下其優先級都是5,每個新產生的線程都繼承了父線程的優先級。setPriority()方法調整優先級。

  • 5,線程的同步

    • Java提供了線程同步機制來防止資源訪問衝突。

    • 線程的同步機制:基本上所有解決多線程資源衝突問題的方法都是採用給定時間只允許一個線程訪問共享資源,這是就需要給共享資源上一道鎖。

      • 同步機制使用synchronized關鍵字。其語法如下:

        synchronized(Object){}
        
        • 通常將共享資源的操作放置在synchronized定義的區域內,這樣當其他線程也獲取到這個鎖時,必須等待鎖被釋放時才能進入該區域。

        • Object爲任意一個對象,每個對象都存在一個標誌位,並且有兩個值,分別爲0和1。一個線程運行到同步塊時首先檢查該對象的標誌位,如果爲0狀態,表明此同步塊中存在其他線程在運行。這時線程處於就緒狀態,直到處於同步塊中的線程執行完畢爲止。這時該對象的標誌位被設置爲1,該線程才能執行同步塊中的代碼,並將Object對象的標誌位設置爲0,防止其他線程執行同步塊中的代碼。

      • 同步方法:

        同步方法就是在方法前面修飾synchronized關鍵字的方法,語法:synchronized void f(){}
        

第十四章 I/O

  • 1,輸入/輸出流

    • Java語言定義了許多類專門負責各種方式的輸入/輸出,這些類都被放在java.io包中。

    • 其中,所有輸入流都是抽象類InputStream(字節輸入流)或抽象類Reader(字符輸入流)的子類;

    • 而所有輸出流都是抽象類OutputStream(字節輸出流)或抽象類Writer(字符輸出流)的子類。

    • InputStream類常用的方法:read();從輸入流中讀取數據的下一個字節。返回0~255範圍內的int字節值。如果因爲已經到達流末尾而沒有可用的字節,則返回-1。

      • read(byte[] b);從輸入流中讀入一定長度的字節,並以整數的形式返回字節數。

      • close();關閉此輸入流並釋放與該流關聯的所有系統資源。

    • 字符流的由來:字節流讀取文字字節數據後,不直接操作而是先查指定的編碼表。獲取對應的文字,再對這個文字進行操作。簡單的說:字節流+編碼表

    • Java中的字符是Unicode編碼,是雙字節的。InputStream是用來處理字節的,並不適合處理字符文本。

    • Reader類中的方法與InputStream類中的方法類似,讀者在需要時可查看JDK文檔。

    • 輸出流:OutStream類是字節輸出流的抽象類,此抽象類是表示輸出字節流的所有類的超類。

      • OutStream類中所有的方法返回void。

      • 常用的方法:

        • write(int b);將指定的字節寫入此輸出流。

        • write(byte[] b);將b個字節從指定的byte數組寫入此輸出流。

        • write(byte[] b,int off,int len);將指定byte數組中從偏移量off開始的len個字節寫入此輸出流。

        • flush();徹底完成輸出並清空緩存區
          close();關閉輸出流。

        • Writer類是字符輸出流的抽象類,所有字符輸出類的實現都是它的子類。

  • 2,File類

    • File類是java.io包中唯一代表磁盤文件本身的對象。File類定義了一些與平臺無關的方法來操作文件,可以通過調用File類中的方法,實現創建、刪除、重命名文件等操作。

    • File類的對象主要用來獲取文件本身的一些信息,如文件所在的目錄、文件的長度、文件讀寫權限等。數據流可以將數據寫入到文件中,文件也是數據流最常用的數據媒體。

    • 文件的創建與刪除:可以使用File類創建一個文件對象。通常使用以下3種構造方法來創建文件對象。

      • File(String pathname);//該構造方法通過將給定路徑名字符串轉換爲抽象路徑名來創建一個新File實例。pathname指路徑名稱(包含文件名)

      • File(String parent,String child);//該構造方法根據定義的父路徑和子路徑字符串(包含文件名)創建一個新的File對象。parent:父路徑字符串。child:子路徑字符串

      • File(File f,String child);//該構造方法根據parent抽象路徑名和child路徑名稱字符串創建一個新File實例。
        f:父路徑對象
        child:子路徑字符串

    • File類提供了很多方法用於獲取一些文件本身信息。

  • 3,文件輸入/輸出流

    • 程序運行期間,大部分數據都在內存中進行操作,當程序結束或關閉時,這些數據將消失。如果要將數據永久保存,可使用文件輸入/輸出流與指定的文件建立連接,將需要的數據永久保存到文件中。

    • FileInputStream類(繼承自InputStream類)與FileOutputStream類(繼承自OutputStream類)都用來操作磁盤文件。

      • FileInputStream類常用的構造方法:FileInputStream(String name);使用給定的文件名name創建一個FileInputStream對象

      • FileInputStream(File file);使用File對象創建FileInputStream對象。

      • FileOutputStream類有與FileInputStream相同的參數構造方法,創建一個FileOutputStream對象時,可以指定不存在的文件名,但此文件不能是一個已被其他程序打開的文件。

    • FileReader和FileWriter字符流對應了FileInputStream和FileOutputStream類。

    • FileReader流順序地讀取文件,只要不關閉流,每次調用read()方法就順序地讀取源中其餘內容,直到源的末尾或流被關閉。

  • 4,帶緩存的輸入/輸出,

    • 緩存是I/O的一種性能優化。緩存流爲I/O流增加了內存緩存區。

    • BufferedInputStream類可以對所有InputStream類進行帶緩存區的包裝以達到性能的優化。

    • 兩個構造方法:BufferedInputStream(InputStream in);該構造方法創建了一個帶有32個字節的緩存流;

    • BufferedInputStream(InputStream in,int size);該構造方法按指定的大小來創建緩存區。

    • BufferedOutputStream類有一個flush()方法用來將緩存區的數據強制輸出完。

    • 兩個構造方法:BufferedOutputStream(OutputStream in);
      BufferedOutputStream(OutputStream in,int size);

    • BufferedReader類與BufferedWriter類分別繼承Reader類與Writer類。這兩個類同樣具有內部緩存機制,並可以以行爲單位進行輸入/輸出。

      • BufferedReader常用的方法:

        • read();讀取單個字符
        • readLine();讀取一個文本行,並將其返回爲字符串。若無數據可讀,則返回null。
      • BufferedWritr類中的方法都返回void。

        常用方法:

        • write(String s,int off,int len);寫入子符串的某一部分。
        • flush();刷新該流的緩存。
        • newLine();寫入一個行分隔符。
    • 在使用BufferedWriter類的write()方法時,數據並沒有立刻被寫入至輸出流,而是首先進入緩存區中。如果想立刻將緩存區中的數據寫入輸出流,一定要調用flush()方法。

  • 5,數據輸入/輸出流

    • 數據輸入/輸出流(DataInputStream類與DataOutputStream類)允許應用程序以與機器無關的方式從底層輸入流中讀取基本Java數據類型。

    • DataInputStream類和DataOutputStream類的構造方法:
      DataInputStream(InputStream in);
      DataOutStream(OutputStream out);

    • DataOutputStream類提供了三種寫入字符串的方法:
      writeBytes(String s);
      writeChars(String s);
      writeUTF(String s);

  • DataOutputStream類只提供了一個readUTF()方法返回字符串
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章