多線程

多線程

進程概述

進程就是正在運行的程序,是系統進行資源分配和調用的獨立單位。
每一個進程都有它自己的內存空間和系統資源。

多進程的意義

單進程計算機只能做一件事情。而我們現在的計算機都可以一邊玩遊戲(遊戲進程),一邊聽音樂(音樂進程),
所以我們常見的操作系統都是多進程操作系統。比如:Windows,Mac和Linux等,能在同一個時間段內執行多個任務。
對於單核計算機來講,遊戲進程和音樂進程是同時運行的嗎?不是。
因爲CPU在某個時間點上只能做一件事情,計算機是在遊戲進程和音樂進程間做着頻繁切換,且切換速度很快,
所以,我們感覺遊戲和音樂在同時進行,其實並不是同時執行的。多進程的作用不是提高執行速度,而是提高CPU的使用率。

什麼是線程

在一個進程內部又可以執行多個任務,而這每一個任務我們就可以看成是一個線程。是程序使用CPU的基本單位。

多線程有什麼意義

多線程的作用不是提高執行速度,而是爲了提高應用程序的使用率。
怎麼理解?
我們程序在運行的使用,都是在搶CPU的時間片(執行權),如果是多線程的程序,那麼在搶到
CPU的執行權的概率應該比較單線程程序搶到的概率要大.那麼也就是說,CPU在多線程程序
中執行的時間要比單線程多,所以就提高了程序的使用率.但是即使是多線程程序,那麼他們
中的哪個線程能搶佔到CPU的資源呢,這個是不確定的,所以多線程具有隨機性.

併發和並行

併發是邏輯上同時發生,指在某一個時間內同時運行多個程序。
並行是物理上同時發生,指在某一個時間點同時運行多個程序。

什麼是併發

指應用能夠交替執行不同的任務, 其實併發有點類似於多線程的原理, 多線程並非是如果你開兩個線程同時執行多個任務, 執行, 就是在你幾乎不可能察覺到的速度不斷去切換這兩個任務, 已達到"同時執行效果", 其實並不是的, 只是計算機的速度太快, 我們無法察覺到而已. 就類似於你, 吃一口飯喝一口水, 以正常速度來看, 完全能夠看的出來, 當你把這個過程以n倍速度執行時..可以想象一下

什麼是並行

指應用能夠同時執行不同的任務, 例:吃飯的時候可以邊吃飯邊打電話, 這兩件事情可以同時執行

多線程

Java程序運行原理

ava命令會啓動java虛擬機,啓動JVM,等於啓動了一個應用程序,也就是啓動了一個進程。該進程會自動啓動一個 “主線程” ,然後主線程去調用某個類的 main 方法。
所以 main方法運行在主線程中。

JVM的啓動是多線程的嗎?

JVM啓動至少啓動了垃圾回收線程和主線程,所以是多線程的。

如何實現多線程

由於線程是依賴進程而存在的,所以我們應該先創建一個進程出來。
而進程是由系統創建的,所以我們應該去調用系統功能創建一個進程。
但是Java是不能直接調用系統功能的,所以,我們沒有辦法直接實現多線程程序。
但是呢?Java可以去調用C/C++寫好的程序來實現多線程程序。
由C/C++去調用系統功能創建進程,然後由Java去調用這樣的東西,
然後提供一些類供我們使用。我們就可以實現多線程程序了。
參考Thread類

多線程程序實現的方式1

我們啓動線程使用不是run方法,而應該是start方法.使該線程開始執行;
Java 虛擬機調用該線程的 run 方法。

 爲什麼要重寫run方法?

這個類是一個線程類,那麼在這個類中我們可不可以寫一些其他的方法呢?
我們可以在寫其他的方法,那麼其他方法中封裝的代碼都是需要被我們線程執行的嗎? 不一定那麼也就是run方法中封裝應該是必須被線程執行的代碼.
run方法中的代碼的書寫原則: 一般是比較耗時的代碼

列:

public static void main(String[] args) {
//在Java中如何開啓一個線程,利用 Thread 這個線程類
//線程 是程序中的執行線程。Java 虛擬機允許應用程序併發地運行多個執行線程。
MyThread th1 = new MyThread();
th1.setName("劉亦菲");
th1.setPriority(1);
th1.start();//開啓線程的方法
MyThread th2 = new MyThread();
th2.setPriority(Thread.MAX_PRIORITY);
th2.setName("林青霞");
th2.start();
//Java中多個線程,執行是隨機性的,因爲我Java採用的線程調度模型是搶佔式調度,線程優先級高的優先使用CPU的執行權
//優先級一樣,就是隨機搶佔

}

public class MyThread extends Thread {br/>@Override
public void run() { //需要線程來執行的方法
//一般run方法裏面寫的就是耗時操作
for (int i = 0; i < 100; i++) {
//System.out.println(this.getName()+"=="+i);

        System.out.println(Thread.currentThread().getName()+"===" + i);

    }
}

}

線程的執行

假如我們的計算機只有一個 CPU,那麼 CPU 在某一個時刻只能執行一條指令,
線程只有得到 CPU時間片,也就是使用權,纔可以執行指令。

線程調度的兩種模式

分時調度模型

所有線程輪流使用 CPU 的使用權,平均分配每個線程佔用 CPU 的時間片

搶佔式調度模型

優先讓優先級高的線程使用 CPU,如果線程的優先級相同,那麼會隨機選擇一個,優先級高的線程獲取的 CPU 時間片相對多一些。 Java使用的是搶佔式調度模型。

設置線程的優先級

public final int getPriority() //獲取線程的優先級
public final void setPriority(int newPriority)//設置線程的優先級

線程休眠

線程休眠: public static void sleep(long millis) 線程休眠

列:

public static void main(String[] args) throws InterruptedException {

    System.out.println("這是一段廣告");
    Thread.sleep(5000);
    System.out.println("bbbbb");
    //Thread(String name) 分配新的 Thread 對象。
    MyThread th1 = new MyThread("林青霞");
    th1.start();
}

public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    for (int i = 0; i < 100; i++) {
        try {
            Thread.sleep(2000);//讓線程休眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //System.out.println(this.getName()+"=="+i);
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

加入線程

加入線程: public final void join()
意思就是: 等待該線程執行完畢了以後,其他線程才能再次執行
注意事項: 在線程啓動之後,在調用方法

列:

public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("劉備");
MyThread th2 = new MyThread("關羽");
MyThread th3 = new MyThread("張飛");

    //join()方法可以將多個線程併發的執行,轉換成串行
    //A:
    //加入線程:
    //public final void join ()
    //意思就是:
    //等待該線程執行完畢了以後, 其他線程才能再次執行
    //注意事項:
    //在線程啓動之後, 在調用方法
    th1.start();
    th1.join();
    th2.start();
    th2.join();
    th3.start();
}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    for (int i = 0; i < 100; i++) {

        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

禮讓線程

禮讓線程: public static void yield(): 暫停當前正在執行的線程對象,並執行其他線程。

列:

public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread("劉備");
MyThread th2 = new MyThread("關羽");
th1.start();
th2.start();

}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    for (int i = 0; i < 100; i++) {
        Thread.yield();//線程禮讓
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

守護線程

守護線程: public final void setDaemon(boolean on):
將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。
該方法必須在啓動線程前調用。

列:

public class MyTest {
public static void main(String[] args) {
Thread.currentThread().setName("劉備");
System.out.println("主線程開始了");
System.out.println("主線程開始了");
System.out.println("主線程開始了");
System.out.println("主線程開始了");

    //A:
    //守護線程:
    //public final void setDaemon ( boolean on):
    //將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。
    //該方法必須在啓動線程前調用。
    //當用戶線程,執行完之後,那麼守護線程,必須裏面死亡
    MyThread th1 = new MyThread("張飛");
    MyThread th2 = new MyThread("關羽");
    //設置爲守護線程 該方法必須在啓動線程前調用。
    th1.setDaemon(true);
    th2.setDaemon(true);
    th1.start();
    th2.start();

    System.out.println("主線程結束了");
    System.out.println("主線程結束了");
    System.out.println("主線程結束了");
    System.out.println("主線程結束了");
    System.out.println("主線程結束了");

}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    for (int i = 0; i < 100; i++) {
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

java用戶線程和守護線程

  • 1.用戶線程和守護線程的區別
    用戶線程和守護線程都是線程,區別是Java虛擬機在所有用戶線程dead後,程序就會結束。而不管是否還有守護線程還在運行,若守護線程還在運行,則會馬上結束。很好理解,守護線程是用來輔助用戶線程的,如公司的保安和員工,各司其職,當員工都離開後,保安自然下班了。
  • 2.用戶線程和守護線程的適用場景
    由兩者的區別及dead時間點可知,守護線程不適合用於輸入輸出或計算等操作,因爲用戶線程執行完畢,程序就dead了,適用於輔助用戶線程的場景,如JVM的垃圾回收,內存管理都是守護線程,還有就是在做數據庫應用的時候,使用的數據庫連接池,連接池本身也包含着很多後臺線程,監聽連接個數、超時時間、狀態等。

  • 3.創建守護線程
    調用線程對象的方法setDaemon(true),設置線程爲守護線程。
    1)thread.setDaemon(true)必須在thread.start()之前設置。
    2)在Daemon線程中產生的新線程也是Daemon的。
    3)不是所有的應用都可以分配給Daemon線程來進行服務,比如讀寫操作或者計算邏輯。
    因爲Daemon Thread還沒來得及進行操作,虛擬機可能已經退出了。

  • 4.Java守護線程和Linux守護進程
    兩者不是一個概念。Linux守護進程是後臺服務進程,沒有控制檯。
    在Windows中,你可以運行javaw來達到釋放控制檯的目的,在Unix下你加&在命令的最後就行了。所以守護進程並非一定需要的。

    線程中斷

    中斷線程
    public final void stop(): 停止線程的運行
    public void interrupt(): 中斷線程(這個翻譯不太好),查看API可得當線程調用wait(),sleep(long time)方法的時候處於阻塞狀態,可以通過這個方法清除阻塞

    列:

    public class MyTest {
    public static void main(String[] args) throws InterruptedException {

    MyThread th1 = new MyThread("張飛");
    th1.start();
    Thread.sleep(20);
    th1.stop();//強制停掉子線程

    }
    }
    public class MyThread extends Thread {
    public MyThread(String name) {
    super(name);
    }

    @Override
    public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    for (int i = 0; i < 100000; i++) {

        System.out.println(Thread.currentThread().getName() + "===" + i);
    
    }

    }
    }

    列2:

    public class MyTest {
    public static void main(String[] args) throws InterruptedException {
    MyThread th1 = new MyThread("張飛");
    th1.start();
    Thread.sleep(1000);
    th1.interrupt();//打斷線程的阻塞狀態,讓線程繼續運行
    }
    }
    public class MyThread extends Thread {
    public MyThread(String name) {
    super(name);
    }

    @Override
    public void run() { //需要線程來執行的方法
    //一般run方法裏面寫的就是耗時操作
    try {
    Thread.sleep(5000); //讓線程休眠,其實是讓線程處於了一種阻塞狀態
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    for (int i = 0; i < 100; i++) {

        System.out.println(Thread.currentThread().getName() + "===" + i);
    
    }

    }
    }

    多線程程序實現的方式2

    實現Runnable接口 這種方式擴展性強 實現一個接口 還可以再去繼承其他類
    優點:可以避免由於Java單繼承帶來的侷限性。

    列:

    public class MyTest {
    public static void main(String[] args) {
    //1. 創建線程的另一種方法是聲明實現 Runnable 接口的類。
    //2. 該類然後實現 run 方法。然後可以分配該類的實例,
    // 3. 在創建 Thread 時作爲一個參數來傳遞並啓動
    MyRunable myRunable = new MyRunable();

    //Thread(Runnable target)
    //分配新的 Thread 對象。
    
    //Thread(Runnable target, String name)
    //分配新的 Thread 對象。
    Thread th = new Thread(myRunable,"王菲");
    th.start();
    Thread th2 = new Thread(myRunable,"謝霆鋒");
    th2.start();

    }
    }
    public class MyRunable implements Runnable { //Runable 任務br/>@Override
    public void run() {
    for (int i = 0; i < 100; i++) {
    // System.out.println(this.getName+"==+i");
    System.out.println(Thread.currentThread().getName() + "==" + i);
    }
    }
    }

    多線程程序的實現方式3

    實現 Callable 接口。 相較於實現 Runnable 接口的方式,方法可以有返回值,並且可以拋出異常。
    執行 Callable 方式,需要 FutureTask 實現類的支持,用於接收運算結果。 FutureTask 是 Future 接口的實現類

    列:

    public class MyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //創建一個線程的方式3
    //A:
    //實現 Callable 接口。相較於實現 Runnable 接口的方式,方法可以有返回值,並且可以拋出異常。
    //
    //B:
    //執行 Callable 方式,需要 FutureTask 實現類的支持,用於接收運算結果。FutureTask 是 Future 接口的實現類
    //
    //C:
    //實現步驟
    //1. 創建一個類實現Callable 接口
    //2. 創建一個FutureTask類將Callable接口的子類對象作爲參數傳進去
    //3. 創建Thread類, 將FutureTask對象作爲參數傳進去
    //4. 開啓線程

    //Callable 返回結果並且可能拋出異常的任務。實現者定義了一個不帶任何參數的叫做 call 的方法。
    //
    //Callable 接口類似於 Runnable,兩者都是爲那些其實例可能被另一個線程執行的類設計的。
    // 但是 Runnable 不會返回結果,並且無法拋出經過檢查的異常。
    
    MyCallable myCallable = new MyCallable();
    FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);
    Thread th = new Thread(integerFutureTask);
    th.start();
    
    //獲取線程執行完之後,返回的結果
    
    Integer integer = integerFutureTask.get();
    System.out.println(integer);

    }
    }
    public class MyCallable implements Callable<Integer> {

    //需要線程來執行的方法br/>@Override
    public Integer call() throws Exception {
    System.out.println(Thread.currentThread().getName()+" aaaaaa");

    return 100;

    }
    }

線程安全問題產生的原因

  • 是否是多線程環境
  • 是否有共享數據
  • 是否有多條語句操作共享數據
    解決方法:
    把多個語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可。
    需要使用同步代碼塊:
    synchronized(對象){//不能在括號了直接new 對象 new 了 就沒效果 要被同步的代碼 ;}
    這個同步代碼塊保證數據的安全性的一個主要因素就是這個對象
    注意這個對象 要定義爲靜態成員變量 才能被所有線程共享
  • 需要這個對象被所有的線程對象所共享
  • 這個對象其實就是一把鎖.
  • 這個對象習慣叫做監視器

    同步代碼塊的方式解決線程安全問題及解釋以及同步的特點及好處和弊端

  • 同步的好處: 同步的出現解決了多線程的安全問題。
  • 同步的弊端: 當線程相當多時,因爲每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率。
    在java編程中,經常需要用到同步,而用得最多的也許是synchronized關鍵字了,
    因爲synchronized關鍵字涉及到鎖的概念,所以先來了解一些相關的鎖知識。
    java的內置鎖:每個java對象都可以用做一個實現同步的鎖,這些鎖成爲內置鎖。
    線程進入同步代碼塊或方法的時候會自動獲得該鎖,在退出同步代碼塊或方法時會釋放該鎖。
    獲得內置鎖的唯一途徑就是進入這個鎖的保護的同步代碼塊或方法。
    java內置鎖是一個互斥鎖,這就是意味着最多隻有一個線程能夠獲得該鎖,
    當線程A嘗試去獲得線程B持有的內置鎖時,線程A必須等待或者阻塞,
    直到線程B釋放這個鎖,如果B線程不釋放這個鎖,那麼A線程將永遠等待下去。
    java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內置鎖是一致的,
    但是,兩個鎖實際是有很大的區別的,對象鎖是用於對象實例方法,或者一個對象實例上的,
    類鎖是用於類的靜態方法或者一個類的class對象上的。
    我們知道,類的對象實例可以有很多個,但是每個類只有一個class對象,
    所以不同對象實例的對象鎖是互不干擾的,但是每個類只有一個類鎖。
    但是有一點必須注意的是,其實類鎖只是一個概念上的東西,並不是真實存在的,
    它只是用來幫助我們理解鎖定實例方法和靜態方法的區別的.

    列:

    public class CellRunable implements Runnable {
    static int piao = 100;
    static Object obj = new Object();
    int i = 0;

    // th2br/>@Override
    public void run() {

    while (true) {
        if (i % 2 == 0) {
            synchronized (CellRunable.class) { //obj 就相當於一把鎖 哪個線程一進入同步代碼塊,就會持有鎖,那麼這個把所不釋放,其他線程就阻塞狀態
                if (piao > 0) {
                    //模擬延遲
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //i-- i++ 先使用 後運算                                              //th3 //th2 100
                    System.out.println(Thread.currentThread().getName() + "正在售賣 " + (piao--) + " 張票");
                }
            }
            //哪個線程出了同步代碼塊就會釋放鎖
        } else {
            maiPiao();
        }
        i++;
    }

    }

    //將 synchronized 加到方法上,我們叫做同步方法
    //同步方法用的是this 這個多對象
    //靜態同步方法使用的是當前類的字節碼文件對象
    public static synchronized void maiPiao() {
    //System.out.println(this);
    // 相當於一把鎖 哪個線程一進入同步代碼塊,就會持有鎖,那麼這個把所不釋放,其他線程就阻塞狀態
    if (piao > 0) {
    //模擬延遲

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //i-- i++ 先使用 後運算                                              //th3 //th2 100
        System.out.println(Thread.currentThread().getName() + "正在售賣 " + (piao--) + " 張票");
    }

    }
    }
    public class MyTest {
    public static void main(String[] args) {
    /*

    • 通過觀察發現,我們發現了一些不合理數據,也就是說在多線程環境下,出現了線程安全問題
    • 出現線程安全的問題:得符號三個條件
    • 1.是否是多線程環境
    • 2.是否有共享數據
    • 3.是否有多條語句在操作這個共享數據
    • 重複票數:由原子性所導致,原子性:不割再分割 i-- 不是原子性操作

    • 出現0票負票:由線程的隨機性所導致的
    • 怎麼解決可以使用同步代碼塊來解決
    • synchronized (鎖對象){
      //需要被同步的代碼

       }
      
       //鎖對象:可以傳任意的所對象
    • */

      CellRunable cellRunable = new CellRunable();
      Thread th1 = new Thread(cellRunable);
      Thread th2 = new Thread(cellRunable);
      Thread th3 = new Thread(cellRunable);
      th1.setName("窗口1");
      th2.setName("窗口2");
      th3.setName("窗口3");
      th1.start();
      th2.start();
      th3.start();
      }
      }

JDK5之後的Lock鎖的概述和使用

  • Lock鎖的概述
    雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,
    但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖,
    爲了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock
  • Lock和ReentrantLock
    void lock()
    void unlock()
    public class CellRunable implements Runnable {
    static int piao = 100;
    static Object obj = new Object();
    static Lock lock = new ReentrantLock();

    列:

    @Override
    public void run() {

    while (true) {
        lock.lock(); //加鎖
        if (piao > 0) {
    
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(Thread.currentThread().getName() + "正在售賣 " + (piao--) + " 張票");
        }
    
        lock.unlock(); //釋放鎖
    }

    }

}
public class MyTest {
public static void main(String[] args) {
/ A:
Lock鎖的概述
雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,
但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖,
爲了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock
B:
Lock和ReentrantLock
void lock ()
void unlock ()
/

    CellRunable cellRunable = new CellRunable();
    Thread th1 = new Thread(cellRunable);
    Thread th2 = new Thread(cellRunable);
    Thread th3 = new Thread(cellRunable);
    th1.setName("窗口1");
    th2.setName("窗口2");
    th3.setName("窗口3");
    th1.start();
    th2.start();
    th3.start();
}

}

死鎖

  • 死鎖問題概述
    如果出現了同步嵌套,就容易產生死鎖問題
    是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象
    同步代碼塊的嵌套案例
    死鎖: 兩個或者兩個以上的線程,在搶佔CPU的執行權的時候,都處於等待狀態
    舉例: 中國人和美國人一起吃飯
    中國人使用的筷子
    美國人使用的刀和叉
    中國人獲取到了美國人的刀
    美國人獲取到了中國人的一根筷子

    列:

    public interface LockeInterface {
    //定義兩把鎖
    public static final Object objA=new Object();
    public static final Object objB=new Object();
    }
    public class MyThread extends Thread {
    boolean b;

    public MyThread(boolean b) {
    this.b = b;
    }

    @Override
    public void run() {
    if (b) {
    synchronized (LockeInterface.objA) {
    System.out.println("true objA 進來了");
    try {
    Thread.sleep(10);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    synchronized (LockeInterface.objB) {
    System.out.println("true objB 進來了");
    }
    }
    } else {
    synchronized (LockeInterface.objB) {
    System.out.println("false objB 進來了");
    synchronized (LockeInterface.objA) {
    System.out.println("false objA 進來了");
    }
    }//
    }
    }
    }
    public class MyTest {
    public static void main(String[] args) {
    //死鎖:
    //兩個或者兩個以上的線程, 在搶佔CPU的執行權的時候, 都處於等待狀態
    MyThread th1 = new MyThread(true);
    MyThread th2 = new MyThread(false);
    th1.start();
    th2.start();
    }
    }

    wait()和sleep()的區別

  • wait()方法和sleep()方法 都可以使線程處於阻塞狀態
  • wait() 可以設置時間量,也可以不設置。
  • sleep() 是要設置休眠的時間
  • sleep() 一旦休眠不會釋放鎖
  • wait() 一旦等待就會釋放鎖

    線程池

  • 線程池概述
    程序啓動一個新線程成本是比較高的,因爲它涉及到要與操作系統進行交互。
    而使用線程池可以很好的提高性能,尤其是當程序中要創建大量生存期很短的線程時,更應該考慮使用線程池。
    線程池裏的每一個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對象來使用。
    在JDK5之前,我們必須手動實現自己的線程池,從JDK5開始,Java內置支持線程池
  • 內置線程池的使用概述
    JDK5新增了一個Executors工廠類來產生線程池,有如下幾個方法
    public static ExecutorService newCachedThreadPool(): 根據任務的數量來創建線程對應的線程個數
    public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化幾個線程
    public static ExecutorService newSingleThreadExecutor(): 初始化一個線程的線程池
    這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者Callable對象代表的線程。它提供瞭如下方法
    Future<?> submit(Runnable task)
    <T> Future<T> submit(Callable<T> task)
  • 使用步驟:
    創建線程池對象
    創建Runnable實例
    提交Runnable實例
    關閉線程池

    列:

    public class MyTest {
    public static void main(String[] args) {
    //線程池:是一個裝有一定數量的線程對象的容器,可以幫我們管理這些線程對象,重複利用線程對象,去執行任務
    //程序開啓一個線程,是比較耗費性能的,因爲他涉及到更操作系統進行交互。
    //JDK1.5之後,幫我們提供好了線程池,我們可以直接拿來用

    //JDK5新增了一個Executors工廠類來產生線程池,有如下幾個方法
    
    //public static ExecutorService newCachedThreadPool ():根據任務的數量來創建線程對應的線程個數
    //public static ExecutorService newFixedThreadPool ( int nThreads):固定初始化幾個線程
    //public static ExecutorService newSingleThreadExecutor ():初始化一個線程的線程池
    //這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者
    
    //ExecutorService 線程池
    //獲取線程池對象
    ExecutorService executorService = Executors.newCachedThreadPool();
    //給線程池提交任務即可
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    //關閉線程池
    executorService.shutdown();

    }
    }
    public class MyRunable implements Runnable{br/>@Override
    public void run() {
    System.out.println(Thread.currentThread().getName()+" 線程任務執行完了");
    }
    }
    public class MyTest2 {
    public static void main(String[] args) {
    //獲取一個線程池,裏面有3個線程對象
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    //給線程池提交任務
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());
    executorService.submit(new MyRunable());

    //關閉線程池
    executorService.shutdown();

    }
    }
    public class MyTest3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //獲取一個線程池。裏面只有一個線程對象
    ExecutorService executorService = Executors.newSingleThreadExecutor();

    Future<Integer> future = executorService.submit(new MyCallable());
    Integer integer = future.get();
    System.out.println(integer);
    
    executorService.shutdown();

    }
    }
    public class MyCallable implements Callable<Integer> {br/>@Override
    public Integer call() throws Exception {
    int i=1;

    return ++i;

    }
    }

    匿名內部類的方式實現多線程程序

列:

public class MyTest3 {
public static void main(String[] args) {
//匿名內部類來開啓線程
//方式1
new Thread() {br/>@Override
public void run() {
System.out.println("線程執行了");
}
}.start();

    //方式2
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("線程執行了");
        }
    }).start();
}

}

定時器

  • 定時器概述
    定時器是一個應用十分廣泛的線程工具,可用於調度多個定時任務以後臺線程的方式執行。
    在Java中,可以通過Timer和TimerTask類來實現定義調度的功能。
  • Timer和TimerTask
    Timer:
    public Timer()
    public void schedule(TimerTask task, long delay):
    public void schedule(TimerTask task,long delay,long period);
    public void schedule(TimerTask task, Date time):
    public void schedule(TimerTask task, Date firstTime, long period):
    TimerTask
    public abstract void run()
    public boolean cancel()
  • 開發中
    Quartz是一個完全由java編寫的開源調度框架。

    列:

    public class MyTest {
    public static void main(String[] args) throws ParseException {
    //定時器:Timer

    // 一種工具,線程用其安排以後在後臺線程中執行的任務。可安排任務執行一次,或者定期重複執行。
    Timer timer = new Timer();
    //可以到時間執行任務
    //void schedule (TimerTask task, Date time)
    //安排在指定的時間執行指定的任務。
    //void schedule (TimerTask task, Date firstTime,long period)
    //安排指定的任務在指定的時間開始進行重複的固定延遲執行。
    //void schedule (TimerTask task,long delay)
    //安排在指定延遲後執行指定的任務。
    //void schedule (TimerTask task,long delay, long period)
    //安排指定的任務從指定的延遲後開始進行重複的固定延遲執行。
    
    //TimerTask由 Timer 安排爲一次執行或重複執行的任務。
    //boolean cancel ()
    //取消此計時器任務。
    MyTimerTask myTimerTask = new MyTimerTask(timer);
    //timer.schedule(myTimerTask, 3000); //2秒之後執行定時任務
    // timer.schedule(myTimerTask,2000,1000);//第一次等2秒執行,後面每隔一秒重複執行。
    //myTimerTask.cancel();取消定時任務
    
    //在指定的日期來執行任務
    String str = "2019-05-17 15:45:00";
    Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
    
    timer.schedule(myTimerTask, date);
    
    // timer.cancel();取消定時器

    }
    }
    public class MyTimerTask extends TimerTask {
    Timer timer;
    public MyTimerTask(Timer timer) {
    this.timer=timer;

    }

    @Override
    public void run() {
    System.out.println("砰~~~ 爆炸了");
    //timer.cancel();
    }
    }

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