Java 高級 --- 多線程快速入門

這世上有三樣東西是別人搶不走的:一是吃進胃裏的食物,二是藏在心中的夢想,三是讀進大腦的書git

多線程快速入門

一、線程與進程區別

  • 每一個正在系統上運行的程序都是一個進程。每一個進程包含一到多個線程。線程是一組指令的集合,或者是程序的特殊段,它能夠在程序裏獨立執行。 因此線程基本上是輕量級的進程,它負責在單個程序裏執行多任務。一般由操做系統負責多個線程的調度和執行。github

  • 使用線程能夠把佔據時間長的程序中的任務放到後臺去處理,程序的運行速度可能加快,在一些等待的任務實現上如用戶輸入、文件讀寫和網絡收發數據等,線程就比較有用了。在這種狀況下能夠釋放一些珍貴的資源如內存佔用等等。數據庫

  • 若是有大量的線程,會影響性能,由於操做系統須要在它們之間切換,更多的線程須要更多的內存空間,線程的停止須要考慮其對程序運行的影響。一般塊模型數據是在多個線程間共享的,須要防止線程死鎖狀況的發生。api

  • 總結:進程是全部線程的集合,每個線程是進程中的一條執行路徑。

二、爲何要使用多線程?

  • (1)、使用多線程能夠減小程序的響應時間。單線程若是遇到等待或阻塞,將會致使程序不響應鼠標鍵盤等操做,使用多線程能夠解決此問題,加強程序的交互性。網絡

  • (2)、與進程相比,線程的建立和切換開銷更小,由於線程共享代碼段、數據段等內存空間。多線程

  • (3)、多核CPU,多核計算機自己就具備執行多線程的能力,若是使用單個線程,將沒法重複利用計算資源,形成資源的巨大浪費。ide

  • (4)、多線程能夠簡化程序的結構,使程序便於維護,一個很是複雜的進程能夠分爲多個線程執行。函數

三、多線程應用場景?

  • 答:主要能體現到多線程提升程序效率。
  • 舉例: 迅雷多線程下載、數據庫鏈接池、分批發送短信等。

四、多線程建立方式

第一種、 繼承Thread類 重寫run方法

class CreateThread extends Thread {
    // run方法中編寫 多線程須要執行的代碼
    publicvoid run() {
        for (inti = 0; i< 10; i++) {
            System.out.println("i:" + i);
        }
    }
}
publicclass ThreadDemo {

    publicstaticvoid main(String[] args) {
        System.out.println("-----多線程建立開始-----");
        // 1.建立一個線程
        CreateThread createThread = new CreateThread();
        // 2.開始執行線程 注意 開啓線程不是調用run方法,而是start方法
        System.out.println("-----多線程建立啓動-----");
        createThread.start();
        System.out.println("-----多線程建立結束-----");
    }

}

  • 調用start方法後,代碼並無從上往下執行,而是有一條新的執行分支性能

  • 注意:畫圖演示多線程不一樣執行路徑。

第二種、實現Runnable接口,重寫run方法

class CreateRunnable implements Runnable {

    @Override
    publicvoid run() {
        for (inti = 0; i< 10; i++) {
            System.out.println("i:" + i);
        }
    }

}
publicclass ThreadDemo2 {
    publicstaticvoid main(String[] args) {
        System.out.println("-----多線程建立開始-----");
        // 1.建立一個線程
        CreateRunnable createThread = new CreateRunnable();
        // 2.開始執行線程 注意 開啓線程不是調用run方法,而是start方法
        System.out.println("-----多線程建立啓動-----");
        Thread thread = new Thread(createThread);
        thread.start();
        System.out.println("-----多線程建立結束-----");
    }
}

第三種、使用匿名內部類方式

System.out.println("-----多線程建立開始-----");
         Thread thread = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i< 10; i++) {
                    System.out.println("i:" + i);
                }
            }
        });
         thread.start();
         System.out.println("-----多線程建立結束-----");

五、使用繼承Thread類仍是使用實現Runnable接口好?

  • 使用實現實現Runnable接口好,緣由實現了接口還能夠繼續繼承,繼承了類不能再繼承。

六、啓動線程是使用調用start方法仍是run方法?

  • 開始執行線程 注意 開啓線程不是調用run方法,而是start方法調用run知識使用實例調用方法。

七、獲取線程對象以及名稱

經常使用線程api方法
start() 啓動線程
currentThread() 獲取當前線程對象
getID() 獲取當前線程ID Thread-編號  該編號從0開始
getName() 獲取當前線程名稱
sleep(long mill) 休眠線程
Stop() 中止線程,
經常使用線程構造函數
Thread() 分配一個新的 Thread 對象
Thread(String name) 分配一個新的 Thread對象,具備指定的 name正如其名。
Thread(Runable r) 分配一個新的 Thread對象
Thread(Runable r, String name) 分配一個新的 Thread對象

八、守護線程

  • Java中有兩種線程,一種是用戶線程,另外一種是守護線程。
  • 用戶線程是指用戶自定義建立的線程,主線程中止,用戶線程不會中止
  • 守護線程當進程不存在或主線程中止,守護線程也會被中止。
  • 使用setDaemon(true)方法設置爲守護線程
public class DaemonThread {

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    System.out.println("我是子線程...");
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {

            }
            System.out.println("我是主線程");
        }
        System.out.println("主線程執行完畢!");
    }

}

九、多線程運行狀態

  •  線程從建立、運行到結束老是處於下面五個狀態之一:新建狀態就緒狀態運行狀態阻塞狀態死亡狀態

新建狀態

  • 當用new操做符建立一個線程時, 例如new Thread(r),線程尚未開始運行,此時線程處在新建狀態。 當一個線程處於新生狀態時,程序尚未開始運行線程中的代碼

就緒狀態

  • 一個新建立的線程並不自動開始運行,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啓動了線程,start()方法建立線程運行的系統資源,並調度線程運行run()方法。當start()方法返回後,線程就處於就緒狀態。
  • 處於就緒狀態的線程並不必定當即運行run()方法,線程還必須同其餘線程競爭CPU時間,只有得到CPU時間才能夠運行線程。由於在單CPU的計算機系統中,不可能同時運行多個線程,一個時刻僅有一個線程處於運行狀態。所以此時可能有多個線程處於就緒狀態。對多個處於就緒狀態的線程是由Java運行時系統的線程調度程序(thread scheduler)來調度的。

運行狀態

  • 當線程得到CPU時間後,它才進入運行狀態,真正開始執行run()方法.
    阻塞狀態線程運行過程當中,可能因爲各類緣由進入阻塞狀態:操作系統

    1>線程經過調用sleep方法進入睡眠狀態;
      2>線程調用一個在I/O上被阻塞的操做,即該操做在輸入輸出操做完成以前不會返回到它的調用者;

       3>線程試圖獲得一個鎖,而該鎖正被其餘線程持有;
       4>線程在等待某個觸發條件;

死亡狀態

  • 有兩個緣由會致使線程死亡:
     - - 1) run方法正常退出而天然死亡,
     - - 2) 一個未捕獲的異常終止了run方法而使線程猝死。
    -  爲了肯定線程在當前是否存活着(就是要麼是可運行的,要麼是被阻塞了),須要使用isAlive方法。若是是可運行或被阻塞,這個方法返回true; 若是線程仍舊是new狀態且不是可運行的, 或者線程死亡了,則返回false.

join()方法做用

  • 當在主線程當中執行到t1.join()方法時,就認爲主線程應該把執行權讓給t1

建立一個線程,子線程執行完畢後,主線程才能執行。

Thread t1 = new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {

                    }
                    System.out.println(Thread.currentThread().getName() + "i:" + i);
                }
            }
        });
        t1.start();
        // 當在主線程當中執行到t1.join()方法時,就認爲主線程應該把執行權讓給t1
        t1.join();
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {

            }
            System.out.println("main" + "i:" + i);
        }
  • 優先級
  • 現代操做系統基本採用時分的形式調度運行的線程,線程分配獲得的時間片的多少決定了線程使用處理器資源的多少,也對應了線程優先級這個概念。在JAVA線程中,經過一個int priority來控制優先級,範圍爲1-10,其中10最高,默認值爲5。下面是源碼(基於1.8)中關於priority的一些量和方法。
class PrioritytThread implements Runnable {

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().toString() + "---i:" + i);
        }
    }
}
public class ThreadDemo4 {

    public static void main(String[] args) {
        PrioritytThread prioritytThread = new PrioritytThread();
        Thread t1 = new Thread(prioritytThread);
        Thread t2 = new Thread(prioritytThread);
        t1.start();
        // 注意設置了優先級, 不表明每次都必定會被執行。 只是CPU調度會有限分配
        t1.setPriority(10);
        t2.start();
        
    }

}

Yield方法

Thread.yield()方法的做用:暫停當前正在執行的線程,並執行其餘線程。(可能沒有效果)
yield()讓當前正在運行的線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行的機會。所以,使用yield()的目的是讓具備相同優先級的線程之間可以適當的輪換執行。可是,實際中沒法保證yield()達到讓步的目的,由於,讓步的線程可能被線程調度程序再次選中。
結論:大多數狀況下,yield()將致使線程從運行狀態轉到可運行狀態,但有可能沒有效果。

總結

  • 1.進程與線程的區別?
    • 答:進程是全部線程的集合,每個線程是進程中的一條執行路徑,線程只是一條執行路徑。
  • 2.爲何要用多線程?
    • 答:提升程序效率
  • 3.多線程建立方式?
      • 答:繼承Thread或Runnable 接口。
  • 4.是繼承Thread類好仍是實現Runnable接口好?
    • 答:Runnable接口好,由於實現了接口還能夠繼續繼承。繼承Thread類不能再繼承。
  • 5.你在哪裏用到了多線程?
    • 答:主要能體現到多線程提升程序效率。
    • 舉例:分批發送短信、迅雷多線程下載等。

總結不易,給個關注吧 https://github.com/yunlongn 個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=10tbrpafl2993

相關文章
相關標籤/搜索