Java線程編程基礎 第一章

 

 

Java線程編程基礎 第一章

 

1、本章任務:建立多線程程序

2、本章知識點:

  •   瞭解Java線程模型
  •   掌握創建線程的方法
  •   設置線程優先級


3、多線程編程概述

  •   Java提供了對多線程編程的內置支持
  •   多線程程序包括能夠併發運行的兩個或多個代碼段

      單線程、多線程對比示例圖:

 

       
    從圖中可以看出:
    1.單線程中的3段代碼只能在不同的時間點執行,
    2.多線程中的3段代碼可以在同一時間點執行

4、進程與線程

  • 進程:一個進程是 一個正在執行的程序 ,目前的操作系統大多都支持同時運行兩個或多個程序,稱爲多任務操作系統
  • 線程:單個程序 可以同時執行 兩個或更多的任務 ,每個任務稱 一個線程 ,進程包含線程
  • 進程是重量級的任務 ,每個進程都有自己獨立的地址空間
  • 線程是輕量級的任務 ,他們共享一個地址空間,共享同一個進程
  • 進程間通信代價昂貴而且受限,線程通信很容易
  • 利用多線程可使你的程序最大限度地利用CPU,因爲空閒時間被限制在最小


5、
Thread類和Runnable接口

    Java的多線程系統構建在Thread類 、方法以及Runnable接口 之上。
    可以通過繼承Thread類或者實現Runnable接口創建一個新線程
   
    Thread類定義了幾個管理線程的方法,常用方法如下:
    方法名            功能
    ====================================
    getName        獲取線程名
    getPriority      獲得線程的優先級
    isAlive            判定線程是否仍在運行
    join                等待一個線程終止
    run                線程入口方法
    sleep             暫停一個線程一段時間
    start              啓動線程

6、程序的主線程


    6.1)主線程描述
    Java程序啓動時 (從main方法開始),一個線程 立刻開始運行,這個線程稱
    爲主線程 ,主線程的重要性體現在如下兩個方面:
        1)它是產生其他子線程的線程
        2)一般情況下,必須是最後一個結束 執行的線程,
             因爲它要執行各種關閉操作


    主線程不但在程序開始時自動創建,也能通過Thread對象來控制,此時需要
    調用:Thread.currentThread()

    6.2)主線程控制示例代碼
   

public static void main(String args[ ]){
        Thread t = Thread.currentThread( ); //獲取當前線程(主線程)
        System.out.println("當前線程:" + t); //輸出:線程名-優先級-線程組名
        //改變主線程名稱
        t.setName("My Thread");         System.out.println("改變之後的名稱:" + t);

        //打印數字1 ~ 5,每隔1秒打印1次
        try{
            for(int i = 1; i <= 5; i++){
                System.out.println(i);
                Thread.sleep(1000); //休眠1000毫秒
            }
        }catch(InterruptedException e){
             System.out.println( " 主線程中斷! " );
        }
    }

 

7、創建線程

    Java定義了兩種 創建線程的方法
    1)可以實現Runnable接口 ,這是創建線程最簡單的方法,實現Runnable接口
         只需一個簡單的run()方法,其聲明如下:
        public void run();
         run()方法是線程的進入點,線程在run方法返回時結束
   
    2)可以繼承Thread類 ,重寫Thread類的run()方法

8、創建線程
方法1:實現Runnable接口

/**
     * 線程類MyThread,實現Runnable接口
     */
    public class MyThread implements Runnable{ //線程類
        //線程入口點
        public void run(){
            try{
                //打印數字1 ~ 5,每隔500毫秒打印一次
                for(int i = 1; i  <= 5; i++){
                    System.out.println("子線程打印:" + i);
                    Thread.sleep(500); //休眠500毫秒
                }
            }catch(InterruptedException e){
                 System.out.println("子線程中斷!");
            }
            System.out.println("子線程執行結束!");
        }
    }
   
    /**
     * 線程類MyThread的測試類
     */
    public class TestMyThread{
        public static void main(String args[ ]){
            //創建線程
            Thread t = new Thread(new MyThread(), "Demo Thread");
            System.out.println("子線程被創建:" + t); //顯示線程信息
            t.start(); //啓動線程
            //主線程中間隔1秒,打印數字1 ~ 5
            try{
                for(int i = 1; i  <= 5; i++){
                    System.out.println("主線程打印:" + i);
                    Thread.sleep(1000); //休眠1000毫秒
                }
            }catch(InterruptedException e){
                    System.out.println("主線程中斷!");
            }
            System.out.println("主線程執行結束!");
        }
    }

 

9、創建線程 方法2:繼承Thread類

   

/**
     * 線程類MyThread1,繼承Thread類
     */
    public class MyThread1 extends Thread{
        public MyThread1(){
            super("Demo Thread"); //線程命名
        }
        //重寫線程入口方法
        public void run(){
            try{
                //打印數字1 ~ 5,每隔500毫秒打印一次
                for(int i = 1; i  <= 5; i++){
                    System.out.println("子線程打印:" + i);
                    Thread.sleep(500); //休眠500毫秒
                }
            }catch(InterruptedException e){
                 System.out.println("子線程中斷!");
            }
            System.out.println("子線程執行結束!");
        }
    }
   
    /**
     * 線程類MyThread1的測試類
     */
    public class TestMyThread1{
        public static void main(String args[ ]){
            //創建線程
            Thread t = new MyThread1();
            System.out.println("子線程被創建:" + t); //顯示線程信息
            t.start(); //啓動線程
            //主線程中間隔1秒,打印數字1 ~ 5
            try{
                for(int i = 1; i  <= 5; i++){
                    System.out.println("主線程打印:" + i);
                    Thread.sleep(1000); //休眠1000毫秒
                }
            }catch(InterruptedException e){
                System.out.println("主線程中斷!");
            }
            System.out.println("主線程執行結束!");
        }
    }

 


10、如何選擇創建線程的方式

    問: Java有兩種創建線程的方式,哪種更好?
   
    1. Thread類定義了多個派生類可以重寫的方法,run()方法只是其中之一,
    所以只有在需要增強或者修改Thread類時才應該使用繼承Thread類方式
   
    2. 如果不想重寫Thread類的run()方法外的其他方法,最好還是簡單的
    實現Runnable接口(推薦)

11、線程的狀態

  • 準備運行狀態:ready to run
  • 運行狀態:running
  • 暫停狀態:suspended 運行中的線程可以暫停,暫停的線程可以重新啓動
  • 阻塞狀態:blocked 線程在等待其他資源時被阻塞

    (這個狀態和視頻播放器的操作非常類似,: )
   
    注意:線程在任何時候都能被終止,一旦終止就不能被重新激活
   
12、啓動多個線程

   

/**
     * 改進的MyThread線程類(改爲自啓動線程)
     */
    public class MyThread implements Runnable {
       
        private Thread t;//線程對象
        public Thread getT() {
            return t;
        }
        /**構造方法*/
        public MyThread(String threadName){           
            //創建線程對象
            this.t = new Thread(this, threadName);
            System.out.println("創建子線程:" + threadName);
            //啓動線程
            this.t.start();
        }
       
        /**
         * 功能:線程入口方法
         */
        public void run() {
           
            Thread t = Thread.currentThread();
            try {
               
                //打印數字1 ~ 5,間隔1秒
                for(int i = 1; i <= 5; i++){
                    System.out.println(t.getName() + " 打印:" + i);
                    //休眠1秒
                    Thread.sleep(1000);
                }               
            } catch (InterruptedException e) {
                System.out.println("子線程中斷!");
            }
           
            System.out.println("子線程結束!");           
        }
    }
   
    /**
     * 多線程測試類
     */
    public class TestMyThread {
        public static void main(String[] args) {
           
            //創建線程對象1
            MyThread t1 = new MyThread("子線程1");
           
            //創建線程對象2
            MyThread t2 = new MyThread("子線程2");
           
            try {               
                //主線程打印數字1 ~ 5,間隔1秒
                for(int i = 1; i <= 5; i++){
                    System.out.println("主線程打印:" + i);
                    //休眠1秒
                    Thread.sleep(1000);
                }
               
            } catch (InterruptedException e) {
                System.out.println("主線程中斷!");
            }           
            System.out.println("主線程結束!");
        }
    }

 

13、使用isAlive()join()

    問題: 主線程一般要最後結束,之前的示例中是通過在main方法中加入sleep()
    使主線程休眠足夠長的時間以確保主線程最後結束,這個解決方式合理嗎?怎樣
    才能知道子線程是否終止?或者說怎樣才能保證主線程最後結束?

    答案: 有兩種方式可以確定一個線程是否結束:
    1)在線程中調用isAlive(),這個方法由Thread定義,如果它調用的線程仍在運行,
         返回true,否則返回false
    2)使用join()方法來等待另一個線程的結束,該方法一直等待直到它調用的線程終止

14、isAlive()和jion()示例

    嘗試運行以下代碼並觀察主線程是否最後結束:

   

public static void main(String[] args) {
        MyThread t1 = new MyThread("子線程1"); // 創建線程1
        MyThread t2 = new MyThread("子線程2"); // 創建線程2
        MyThread t3 = new MyThread("子線程3"); // 創建線程3

            //顯示線程是否運行
        System.out.println("子線程1是否運行:" + t1.getT().isAlive());
        System.out.println("子線程2是否運行:" + t2.getT().isAlive());
        System.out.println("子線程3是否運行:" + t3.getT().isAlive());
        // 主線程等待子線程結束
        try {
            System.out.println("等待子線程結束…");
            t1.getT().join(); // 等待t1線程結束
            t2.getT().join(); // 等待t2線程結束
            t3.getT().join(); // 等待t3線程結束
        } catch (InterruptedException e) {
            System.out.println("主線程中斷!");
        }
        //顯示線程是否運行
        System.out.println("子線程1是否運行:" + t1.getT().isAlive());
        System.out.println("子線程2是否運行:" + t2.getT().isAlive());
        System.out.println("子線程3是否運行:" + t3.getT().isAlive());
        System.out.println("主線程執行結束!");
    }

 


15、線程優先級概述

    Java給每個線程分配一個優先級,以決定哪個線程可以優先分配CPU時間
    優先級是一個整數,用於指定線程的相對優先程度
    優先級可以決定什麼時候從一個運行中的線程切換到另一個線程,切換規則如下:

  •   一個線程自願釋放控制(放棄、睡眠或者阻塞),所有其他線
      程被檢查,高優先級線程被分配CPU時間
  •   一個線程可以被另一個高優先級線程搶佔資源,稱爲搶佔式多任務

    注意:具有同一優先級的線程競爭CPU時間,不同操作系統的處理方式上存在差別

16、線程優先級設置

  • 線程調度器使用線程優先級以決定什麼時候允許運行
  • 理論上高優先級的線程將比低優先級的線程得到更多CPU時間
  • 低優先級線程在運行時,高優先級的線程會搶佔低優先級線程的執行權
  • 設置線程優先級,使用setPriority()方法:

    final void setPriority(int level);
    參數level爲線程的優先級 ,其值在1 ~ 10 之間


    Thread類提供瞭如下常量方便優先級的設置
    Thread.MIN_PRIORITY == 1
    Thread.MAX_PRIORITY == 10
    Thread.NORM_PRIORITY == 5


17、線程優先級示例

   

/**
     * 功能:計數器線程
     */
    public class Clicker implements Runnable {
       
        private int click = 0;//計數值        
        private Thread t;//線程對象        
        private volatile boolean running = true;//運行開關
       
        public int getClick() {
            return click;
        }       
        public Thread getT() {
            return t;
        }       
        /**構造方法,參數爲優先級*/
        public Clicker(int priority){
            this.t = new Thread(this);
            this.t.setPriority(priority);//設置線程優先級
        }
       
        public void run() {           
            while(this.running){               
                this.click++;//計數器累加
            }
        }
       
        public void start(){
            this.t.start();//啓動線程
        }       
        public void stop(){
            this.running = false;//結束線程
        }       
    }
   
    /**
     * 計數器線程測試類
     */
    public class TestClicker {
        public static void main(String[] args) {
          
            //創建線程,設置4種檔次的優先級
            Clicker hi = new Clicker(Thread.NORM_PRIORITY + 2);
            Clicker hi1 = new Clicker(Thread.NORM_PRIORITY + 4);
            Clicker lo = new Clicker(Thread.NORM_PRIORITY - 2);
            Clicker lo1 = new Clicker(Thread.NORM_PRIORITY - 4);           
            //啓動線程
            hi.start();
            hi1.start();
            lo.start();
            lo1.start();
           
            try {               
                Thread.sleep(2000);//休眠2秒                
                //結束線程
                hi.stop();
                hi1.stop();
                lo.stop();
                lo1.stop();
                //等待線程結束
                hi.getT().join();
                hi1.getT().join();
                lo.getT().join();
                lo1.getT().join();               
            } catch (InterruptedException e) {
                System.out.println("主線程中斷!");
            }           
            //顯示計數器值
            System.out.println("lo1計數器:" + lo1.getClick());
            System.out.println("lo計數器:" + lo.getClick());
            System.out.println("hi計數器:" + hi.getClick());
            System.out.println("hi1計數器:" + hi1.getClick());
            System.out.println("主線程結束!");           
        }
    }

 

18、總結

  • 進程與線程的區別?
  • 如何獲取主線程?
  • 創建線程的兩種方式?
  • 獲知線程是否運行使用哪個方法?
  • 等待線程執行結束使用哪個方法?
  • 線程優先級的範圍是什麼?

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章