多線程編程、Java I/O系統和Java圖形介面編程

來源:互聯網
上載者:User

標籤:

多線程編程:

一個正在啟動並執行程式通常稱為一個進程,每一個任務稱為一個線程,中能夠在一個程式內運行多線程的程式稱為多線程程式。

線程與進程的區別:①每個進程都需要作業系統為其分配獨立的記憶體空間;

         ②而同一個進程中的所有線程都在同一記憶體空間工作,這些線程可以共用同一塊記憶體和系統資源。

線程的建立:

1)通過繼承Thread類來建立線程:

  ①建立一個繼承Thread類的類;

  ②在建立的Thread子類中重寫run()方法,在方法中寫入想要線程啟動並執行代碼;

  ③建立Thread子類的執行個體;

  ④通過調用該執行個體上的start()方法,開始運行線程。

2)通過實現Runnable介面來建立線程:

  ①建立一個類實現Runnable介面,用於代表我們需要線程完成的任務;

  ②在Runnable指定的方法內放入想要線上程中執行的代碼。

  ③建立一個Runnable類的執行個體;

  ④建立一個Thread對象,將Runnable的執行個體作為構造器參數傳進去;

  ⑤通過調用Thread類的執行個體的start()方法,開始執行線程。

兩種方式的聯絡與區別:

  聯絡:Thread類實現了Runnable介面,也就是說Thread類也是Runnable介面的一個子類;

  區別:實現Runnable介面的方式相對於繼承Thread類的方式來說有幾個顯著優勢

     ①使用Runnable介面可以將虛擬CPU(Thread類)與線程要完成的任務有效分離,較好的的體現了物件導向設計的基本原則;

     ②可以避免Java單繼承的局限。

線程的優先順序:

  線程啟動並執行順序並不是以該線程建立的先後順序而定的,而是以優先順序來決定。

除了使用sleep()方法讓線程暫停運行一段時間外,還可以使用yield()方法讓線程停止運行。

線程同步:

  當兩個以上的線程需要訪問共用資源時,我們必須確定在同一時間點只有一個線程能夠存取共用資源,而運行這個目標的過程就稱為同步。

  1.同步塊:Synchronized ( 取得鎖的對象 ){

         //需要鎖定的代碼

       }

   1)當某個線程在運行這個塊時,別的線程無法使用這個塊的代碼;

   2)當一個線程取得“對象鎖”時,如果其他線程也想要取得該對象鎖,它們必須等到對象鎖被釋放。

  2.同步方法:eg:    Synchronized void PrintValue(){

             System.out.println();

           }

線程生命週期: 

線程狀態轉換圖:

1、建立狀態

用new關鍵字和Thread類或其子類建立一個線程對象後,該線程對象就處於新生狀態。處於新生狀態的線程有自己的記憶體空間,通過調用start方法進入就緒狀態(runnable)。

注意:不能對已經啟動的線程再次調用start()方法,否則會出現java.lang.IllegalThreadStateException異常。

2、就緒狀態

處於就緒狀態的線程已經具備了運行條件,但還沒有分配到CPU,處於線程就緒隊列(儘管是採用隊列形式,事實上,把它稱為可運行池而不是可運行隊列。因為cpu的調度不一定是按照先進先出的順序來調度的),等待系統為其分配CPU。等待狀態並不是執行狀態,當系統選定一個等待執行的Thread對象後,它就會從等待執行狀態進入執行狀態,系統挑選的動作稱之為“cpu調度”。一旦獲得CPU,線程就進入運行狀態並自動調用自己的run方法。

提示:如果希望子線程調用start()方法後立即執行,可以使用Thread.sleep()方式使主線程睡眠一夥兒,轉去執行子線程。

3、運行狀態

處於運行狀態的線程最為複雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。

處於就緒狀態的線程,如果獲得了cpu的調度,就會從就緒狀態變為運行狀態,執行run()方法中的任務。如果該線程失去了cpu資源,就會又從運行狀態變為就緒狀態。重新等待系統分配資源。也可以對在運行狀態的線程調用yield()方法,它就會讓出cpu資源,再次變為就緒狀態。

 

當發生如下情況是,線程會從運行狀態變為阻塞狀態:

①、線程調用sleep方法主動放棄所佔用的系統資源

②、線程調用一個阻塞式IO方法,在該方法返回之前,該線程被阻塞

③、線程試圖獲得一個同步監視器,但更改同步監視器正被其他線程所持有

④、線程在等待某個通知(notify)

⑤、程式調用了線程的suspend方法將線程掛起。不過該方法容易導致死結,所以程式應該盡量避免使用該方法。

 

當線程的run()方法執行完,或者被強制性地終止,例如出現異常,或者調用了stop()、desyory()方法等等,就會從運行狀態轉變為死亡狀態。

4、阻塞狀態

處於運行狀態的線程在某些情況下,如執行了sleep(睡眠)方法,或等待I/O裝置等資源,將讓出CPU並暫時停止自己的運行,進入阻塞狀態。 

在阻塞狀態的線程不能進入就緒隊列。只有當引起阻塞的原因消除時,如睡眠時間已到,或等待的I/O裝置空閑下來,線程便轉入就緒狀態,重新到就緒隊列中排隊等待,被系統選中後從原來停止的位置開始繼續運行。有三種方法可以暫停Threads執行:

5、死亡狀態

當線程的run()方法執行完,或者被強制性地終止,就認為它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能複生。 如果在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。

推薦在run方法中使用控制迴圈條件的方式來結束一個線程。

wait:告訴當前線程放棄對象鎖並進入等待狀態,直到其他線程進入同一對象鎖並調用notify為止。

notify:喚醒同一對象鎖中調用wait的第一個線程。

notifyAll:喚醒同一對象鎖中調用wait的所有線程,具有最高優先順序的線程首先被喚醒並執行。

 

補充:

     線程睡眠——sleep

      如果我們需要讓當前正在執行的線程暫停一段時間,並進入阻塞狀態,則可以通過調用Thread的sleep方法    

    線程讓步——yield

      yield()方法和sleep()方法有點相似,它也是Thread類提供的一個靜態方法,它也可以讓當前正在執行的線程暫停,讓出cpu資源給其他的線程。但是和sleep()方法      不同的是,它不會進入到阻塞狀態,而是進入到就緒狀態。yield()方法只是讓當前線程暫停一下,重新進入就緒的線程池中,讓系統的線程調度器重新調度器重新調度      一次,完全可能出現這樣的情況:當某個線程調用yield()方法之後,線程調度器又將其調度出來重新進入到運行狀態執行。實際上,當某個線程調用了yield()方法暫停      之後,優先順序與當前線程相同,或者優先順序比當前線程更高的就緒狀態的線程更有可能獲得執行的機會,當然,只是有可能,因為我們不可能精確的幹涉cpu調度程。

線程的完整的生命週期:

 

圖片說明:

  1、線程的實現有兩種方式,一是繼承Thread類,二是實現Runnable介面,但不管怎樣,當我們new了這個對象後,線程就進入了初始狀態;

  2、當該對象調用了start()方法,就進入可運行狀態;

  3、進入可運行狀態後,當該對象被作業系統選中,獲得CPU時間片就會進入運行狀態;

  4、進入運行狀態後情況就比較複雜了:

    ①run()方法或main()方法結束後,線程就進入終止狀態;

    ②當線程調用了自身的sleep()方法或其他線程的join()方法,就會進入阻塞狀態(該狀態既停止當前線程,但並不釋放所佔有的資源)。當sleep()結束或join()結束後,該線       程進入可運行狀態,繼續等待OS分配時間片;

    ③線程調用了yield()方法,意思是放棄當前獲得的CPU時間片,回到可運行狀態,這時與其他進程處於同等競爭狀態,OS有可能會接著又讓這個進程進入運行狀態;

    ④當線程剛進入可運行狀態(注意,還沒運行),發現將要調用的資源被synchroniza(同步),擷取不到鎖標記,將會立即進入鎖池 狀態,等待擷取鎖標記(這時的鎖池裡也       許已經有了其他線程在等待擷取鎖標記,這時它們處於隊列狀態,既先到先得),一旦線程獲得鎖標記後,就轉入可運行狀態,等待OS分配CPU時間片;

    ⑤當線程調用wait()方法後會進入等待隊列(進入這個狀態會釋放所佔有的所有資源,與阻塞狀態不同),進入這個狀態後,是不能自動喚 醒的,必須依靠其他線程調用         notify()或notifyAll()方法才能被喚醒(由於notify()只是喚醒一個線程,但我們由不能確定具體喚醒的是哪一個線程,也許我們需要喚醒的線程不能夠被喚醒,因此在實       際使用時,一般都用notifyAll()方法,喚醒有所線程),線程被喚醒後會進入鎖 池,等待擷取鎖標記。

Java I/O系統:

1.  Java類庫提供了大量類,可以協助我們從不同的裝置讀取資料,並儲存或輸出到不同的裝置中。這些類同一放在java.io包和java.nio包中,統稱為java I/O系統。

2.  流:我們可以將流理解為傳送資料的管道。

    流是java I/O的基礎,是java語言對 I/O 的一個最基本的抽象,其原因:流的兩個最基本特性:①含有流質。流質就是資料;

                                               ②有方向。方向就是讀或寫,即針對裝置進行資訊的輸入和輸出。

    流的分類:

      1)輸入資料流和輸出資料流——按流的方向分

        輸入資料流:從資料來源讀取資料到程式中。只能讀不能寫。io包中的輸入資料流都繼承自抽象類別OutputStream或Reader 。

        輸出資料流:將資料從程式寫入資料目的地。只能寫不能讀。io包中的輸出資料流都繼承自抽象類別InputStream或Writer 。

      2)位元組流和字元流——流按處理資料的最小單位的不同分

        位元組流:以byte為最小單位進行資料傳送。io包中的位元組流都繼承自抽象類別InputStream或OutputStream 。

        字元流:以char為最小單位進行資料傳送。io包中的位元組流都繼承自抽象類別Reader 或Writer 。

      3)節點流和處理流——按流的功能分

        節點流:又叫低級流,是可以直接 從 / 向 一個特定的資料來源 讀 / 寫 資料的流 。

          處理流:又叫進階流,處理流不能直接連接到裝置,而是串連在已存在的流之上,通過對資料的處理為程式提供更強大的 讀 / 寫 功能 。

      如何分辨進階流、低級流:  如果類的構造器帶有一個已存在的流作為參數,那它就是進階流 。

3.  I/O庫中類具有對成性:1)輸入資料流和輸出資料流的對稱;

            2)位元組流和字元流的對稱。

I/O操作步驟:

      ①建立流;

      ②操作流;

      ③關閉流。

4.  檔案類:將File對象看成是代表一個檔案或目錄的名稱和位置的字串。

      File類中的常用方法:

       boolean canExecute() 測試應用程式是否可以執行此抽象路徑名表示的檔案。

       boolean canRead() 測試應用程式是否可以讀取此抽象路徑名表示的檔案。

         boolean canWrite() 測試應用程式是否可以修改此抽象路徑名表示的檔案。

         int compareTo(File pathname) 按字母順序比較兩個抽象路徑名。

         boolean createNewFile() 若且唯若不存在具有此抽象路徑名指定名稱的檔案時,不可分地建立一個新的空檔案。

         static File createTempFile(String prefix, String suffix) 在預設臨時檔案目錄中建立一個空檔案,使用給定首碼和尾碼產生其名稱。

         static File createTempFile(String prefix, String suffix, File directory) 在指定目錄中建立一個新的空檔案,使用給定的首碼和尾碼字串產生其名稱。

       boolean delete() 刪除此抽象路徑名表示的檔案或目錄。

       void deleteOnExit() 在虛擬機器終止時,請求刪除此抽象路徑名表示的檔案或目錄。

       boolean equals(Object obj) 測試此抽象路徑名與給定對象是否相等。 boolean exists() 測試此抽象路徑名表示的檔案或目錄是否存在。

       String getName() 返回由此抽象路徑名表示的檔案或目錄的名稱。

       boolean isAbsolute() 測試此抽象路徑名是否為絕對路徑名。

       boolean isDirectory() 測試此抽象路徑名表示的檔案是否是一個目錄。

       boolean isFile() 測試此抽象路徑名表示的檔案是否是一個標準檔案。

         boolean isHidden() 測試此抽象路徑名指定的檔案是否是一個隱藏檔案。

       long lastModified() 返回此抽象路徑名表示的檔案最後一次被修改的時間。

       long length() 返回由此抽象路徑名表示的檔案的長度。

        String[] list() 返回一個字串數組,這些字串指定此抽象路徑名表示的目錄中的檔案和目錄。

       Boolean mkdir()  建立目錄。

5.  位元組流:用於處理二進位檔案

    java.io.InputStream類中的常用方法:

       int available()  返回可從該輸入資料流中讀取的位元組數

       void close()  關閉輸入資料流

       int read( byte[] b )從輸入資料流中讀取一些位元組,並將這些位元組存入緩衝數組 b ,讀取完畢返回 -1

       int read( byte[] b, int off, int len )從輸入資料流制定位置off開始讀取資料的前len個位元組,並將其讀入位元組型資料中 ,讀取完畢返回 -1

5.  字元流:用於處理文字檔

       Reader:  BufferedReader

              CharArrayReader

            FilterReader

            InputStreamReader

            PipedReader

            StringReader

       Writer:    BufferedWriter

              CharArrayWriter

            FilterWriter

            InputStreamWriter

            PipedWriter

            StringWriter

            PrintWriter

5.  字元流:基於對象的讀寫,由ObjectInputStream / ObjectOutputStream來提供

      讀寫對象:①對應的類必須實現java.io.Serializable介面;

           ②類被序列化之後通過ObjectInputStream / ObjectOutputStream對對象進行讀寫。

      在定義類是用 transient 修飾屬性則該屬性不能被執行個體化。這樣有利於保護資料安全性。

      實現介面後使用ObjectInputStream 類將對象拆成位元組序列的過程稱為序列化;使用 ObjectOutputStream 類將位元組序列還原稱對象的過程稱為還原序列化。

Java圖形介面編程:

1.Java API中提供了兩套組件用於支援編寫圖形化使用者介面,包括AWT 和 Swing。

    AWT: 定義在你java.awt包中

        ① AWT嚴重依賴於下層作業系統——外觀取決於程式啟動並執行平台;

        ② 效率低,AWT組件也被稱為重量級組件;

        ③ 開發速度較快。

    Swing:

        ①輕量級GUI組件;

        ②有更好的平台無關性和可移植性;

2.容器和組件:Swing視圖部分由兩個基本元素組成:

    ①組件:獨立的可視化控制項,例如按鈕、文字欄位;

    ②容器:一種設計用放置其他組件的特殊類型的組件。

    一個組件要顯示出來,就必須放在一個容器內。因此所有的Swing GUI組件必須至少有一個組件。組件三要素:內容、外觀顯示、行為。

 add():將組件添加到該容器中;

 removeAll():移掉容器中的全部組件;

 remove( Component c ):移掉容器中參數指定的組件;

 組件在容器中的布局:

    1)FlowLayout管理器(JPanel預設):流式布局管理器,組件以從左至右的流式循序配置

      三個構造器:public FlowLayout()

            public FlowLayout( int align )  align為對其方式:FlowLayout.CENTER、FlowLayout.RIGHT、LEFT

            oublic FlowLayout( int align, int hgap, int vgap )  hgap:水平間距, vgap:垂直間距

    2)BorderLayout管理器(JFrame預設):邊框布局管理器,將容器分為東南西北中五個地區。每個地區只能添加一個組件,組件大小由地區決定

      二個構造器:public Borderayout()

            oublic Borderayout( int hgap, int vgap )  hgap:水平間距, vgap:垂直間距

      3)GridLayout管理器:網格布局管理器,將容器分為行和列的網格

      三個構造器:public GridLayout()

            public GridLayout( int rows, int cols )

            oublic GridLayout( int rows, int cols, int hgap, int vgap ) rows:行數目, cols:列數目

    4)BoxLayout管理器:盒式布局管理器,組件按照垂直或水平方式顯示

      一個構造器:public GridLayout( Container target, int axis )為指定目標和給定的軸建立一個新的BoxLayout管理器  

             axis:BoxLayout.X_AXIS:沿x軸方向從左至右水平放置;   

                BoxLayout.Y_AXIS:沿y軸方向從上到下放置。

3.事件驅動編程:

 1)事件和事件來源

    ①事件:用於描述發生了什麼事;

     事件來源:產生一個事件並觸發它的組件被稱為事件來源。

    ②如果一個組件可以觸發一個事件,那麼該組件的任何子類都可以觸發相同類型的時間。

 2)時間監聽器、註冊和處理事件

    成為事件監聽器:

      ①建立一個監聽對象,監聽對象必須是對應事件監聽器介面的執行個體;

      ②將監聽對象註冊到事件來源上,註冊方法取決於事件類型。通常對於Xxx Event,對應的註冊方法為addXxxListener.

 3)定義監聽器類的可選方法:

    ①讓GUI程式本身實現監聽器介面;

    ②使用成員內部類定義監聽器類;

    ③使用匿名內部類定義監聽器類。

      

多線程編程、Java I/O系統和Java圖形介面編程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.