Java 多線程(一)—— 概念的引入


並發和並行

  並行:指兩個或多個時間在同一時刻發生(同時發生);

  並發:指兩個或多個事件在一個時間段內發生。

  在操作系統中,安裝了多個程序,並發指的是在一段時間內宏觀上有多個程序同時運行,這在單 CPU 系統中,每一時刻只能有一道程序執行,即微觀上這些程序是分時的交替運行,只不過是給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的。

  而在多個 CPU 系統中,則這些可以並發執行的程序便可以分配到多個處理器上(CPU),實現多任務並行執行,即利用每個處理器來處理一個可以並發執行的程序,這樣多個程序便可以同時執行。

  目前電腦市場上說的多核 CPU,便是多核處理器,核 越多,並行處理的程序越多,能大大的提高電腦運行的效率。

注意:單核處理器的計算機肯定不能並行的處理多個任務,只能是多個任務交替的在單個 CPU 上運行。

 

進程和線程

  進程:是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。

  線程:進程內部的一個獨立執行單元;一個進程可以同時並發的運行多個線程,可以理解為一個進程便相當於一個單 CPU 操作系統,而線程便是這個系統中運行的多個任務。

 

注意:1、因為一個進程中的多個線程是並發運行的,那么從微觀角度看也是有先后順序的,哪個線程執行完全取決於 CPU 的調度,程序員是干涉不了的。而這也就造成的多線程的隨機性。

   2、Java 程序的進程里面至少包含兩個線程,主進程也就是 main()方法線程,另外一個是垃圾回收機制線程。每當使用 java 命令執行一個類時,實際上都會啟動一個 JVM,每一個 JVM 實際上就是在操作系統中啟動了一個線程,java 本身具備了垃圾的收集機制,所以在 Java 運行時至少會啟動兩個線程。

   3、由於創建一個線程的開銷比創建一個進程的開銷小的多,那么我們在開發多任務運行的時候,通常考慮創建多線程,而不是創建多進程。

     4、多線程是為了同步完成多個任務,不是為了提高程序運行效率,而是通過提高資源使用效率來提高系統的效率。比如做一個添加功能,需要向很多用戶發送短信和郵件,如果需要一分鍾,不能能讓用戶等一分鍾才響應,可以使用多線程做后台發送,如果是單核,則多個任務交替的在單個 CPU 上運行,理論上比不使用多線程耗時多,因為CPU切換任務需要耗費時間,如果是多核,則可以提高資源的使用效率,多核可以充分利用上。

  

如何創建多線程

第一種方法:繼承 Thread 類

步驟:1、定義一個線程類 A 繼承於 java.lang.Thread 類

   2、在 A 類中覆蓋 Thread 類的 run() 方法

   3、在 run() 方法中編寫需要執行的操作

   4、在 main 方法(線程)中,創建線程對象,並啟動線程

      創建線程類:A類 a = new A()類;

      調用 start() 方法啟動線程:a.start();

/**
 * @author: ChenHao
 * 創建多線程的第一種方式,繼承java.lang.Thread類
 * @Description:創建一個子線程,完成1-100之間自然數的輸出。同樣,主線程執行同樣的操作
 * @Date: Created in 10:50 2018/10/29
 */
public class TestThread {
    public static void main(String [] args){
        SubThread subThread1=new SubThread();
        SubThread subThread2=new SubThread();
        //調用線程的start(),啟動此線程;調用相應的run()方法
        subThread1.start();
        subThread2.start();
        //一個線程只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則拋出異常
        //subThread1.start();
        //不能通過Thread實現類對象的run()去啟動一個線程,此時只是主線程調用方法而已,並沒有啟動線程
        //subThread1.run();
        for (int i=0;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
//1.創建一個繼承Thread的子類
class SubThread extends Thread{
    //2.重寫run方法,方法內實現此子線程要完成的功能
    @Override
    public void run(){
        for (int i=0;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

注意:1、不能通過Thread實現類對象的run()去啟動一個線程,此時只是主線程調用方法而已,並沒有啟動線程,要啟動線程,必須通過Start()方法

   2、一個線程只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則拋出異常,所以一個線程調用兩次start()會報異常

 

第二種方法:實現 Runnable 接口

1、Runnable接口應由任何類實現,其實例將由線程執行。 該類必須定義一個無參數的方法,稱為run 。 
2、該接口旨在為希望在活動時執行代碼的對象提供一個通用協議。此類整個只有一個 run() 抽象方法

 

 

 

步驟:1、定義一個線程類 A 實現於 java.lang.Runnable 接口(注意:A類不是線程類,沒有 start()方法,不能直接 new A 的實例啟動線程)

   2、在 A 類中覆蓋 Runnable 接口的 run() 方法

   3、在 run() 方法中編寫需要執行的操作

   4、在 main 方法(線程)中,創建線程對象,並啟動線程

      創建線程類:Thread t = new Thread( new A類() ) ;

      調用 start() 方法啟動線程:t.start();

package main.java.Thread;

/**
 * @author: ChenHao
 * @Description:創建多線程的方式二:實現runnable
 * 對比一下繼承的方式 VS 實現的方式
 * 哪個方式好?實現的方式優於繼承的方式
 * why?   ①避免java單繼承的局限性
 *        ②如果多個線程要操作同一份資源,更適合使用實現的方式
 * @Date: Created in 10:50 2018/10/29
 */
public class TestThread2 {
    public static void main(String [] args){
        //此程序存在線程的安全問題,打印車票時,會出現重票、錯票,后面線程同步會講到
        Window window=new Window();
        Thread thread1=new Thread(window,"窗口一");
        Thread thread2=new Thread(window,"窗口二");
        Thread thread3=new Thread(window,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

class Window implements  Runnable{
    int ticket=100;
    @Override
    public void run(){
        while (true){
            if(ticket > 0){
                try {
                    Thread.currentThread().sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"售票,票號為:"+ticket--);
            }else {
                break;
            }
        }
    }
}

哪個方式好?實現的方式優於繼承的方式

  why? ①避免java單繼承的局限性
     ②如果多個線程要操作同一份資源,更適合使用實現的方式

注意:此程序存在線程的安全問題,打印車票時,會出現重票、錯票,下一篇線程同步會講到

 第三種方法:使用匿名內部類創建線程

public static void main(String[] args) {
    for(int i = 0 ; i < 10 ; i++){
        System.out.println("玩游戲"+i);
        if(i==5){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i = 0 ; i < 10 ;i++){
                        System.out.println("播放音樂"+i);
                    }
                }
            }).start();
        }
    }
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM