Java Thread


六、  Java Thread

 

1.  基本概念

1. 程序(Program):靜態概念
* 程序是指一系列指令的有序集合。指令序列是順序執行的,直到一條跳轉指令(或轉移指令)被執行,或者一個中斷出現

2. 進程(Process):動態概念
* 狹義定義:進程是正在運行的程序的實例(an instance of a computer program that is being executed)
* 廣義定義:進程是一個具有一定獨立功能的程序關於某數據集合上的一次運行活動
* 進程是系統進行資源分配的基本單位,是操作系統結構的基礎
* 通常在一個進程中可以包含若干個線程

3. 線程(Thread):動態概念
* 線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位
* 線程與資源分配無關,它屬於某一個進程,並與進程內的其他線程一起共享該進程的全部系統資源
* 在引入線程的操作系統中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調度的基本單位
* 由於線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提高系統內多個程序間並發執行的程度,從而顯著提高系統資源的利用率和吞吐量
* 近年來推出的通用操作系統都引入了線程,以便進一步提高系統的並發性,並把它視為現代操作系統的一個重要指標

4. 時間片(Timeslice)
* 又稱處理器片(processor slice),是分時操作系統分配給每個正在運行的進程微觀上的一段CPU時間(在搶占內核中是:從進程開始運行直到被搶占的時間)
* 現代操作系統(如:Windows、Linux 等)允許同時運行多個進程 —— 例如,你可以在打開音樂播放器聽音樂的同時用瀏覽器瀏覽網頁並下載文件
* 事實上,由於一台計算機通常只有一個CPU,所以永遠不可能真正地同時運行多個任務
* 這些進程“看起來像”同時運行的,實則是輪番穿插地運行,由於時間片通常很短(在 Linux 上為 5ms-800ms),所以用戶不會感覺的到

 

2.  一個線程的生命周期

 

3.  線程的創建

3.1  繼承 Thread 類,Thread 是 Runnable 接口的實現類

/**
 * 1.創建線程:繼承 Thread 類 + 重寫 run();
 * 2.使用線程:實例化線程對象 + 對象.start();
 *
 * 模擬龜兔賽跑
 */
public class Rabbit extends Thread{
    @Override
    public void run(){
        //線程體
        for(int i=0;i<2;i++){
            System.out.println("兔子跑了"+i+"步");
        }
    }  
}
public class Tortoise extends Thread{
    @Override
    public void run(){
        //線程體
        for(int i=0;i<2;i++){
            System.out.println("烏龜跑了"+i+"步");
        }
    } 
}
public class Test{
    public static void main(String[] args) {
        //創建子類對象
        Rabbit rab = new Rabbit();
        Tortoise tor = new Tortoise();

        //調用start方法
        rab.start();//不要調用 run 方法,不然就是普通的方法調用
        tor.start();
        
        for(int i=0;i<2;i++){
            System.out.println("main==>"+i);
        }
    }
}

// 運行結果,每一次運行結果是不同的
main==>0
兔子跑了0步
兔子跑了1步
烏龜跑了0步
烏龜跑了1步
兔子跑了2步
main==>1
兔子跑了3步
烏龜跑了2步
兔子跑了4步
main==>2
兔子跑了5步
烏龜跑了3步
main==>3
烏龜跑了4步
main==>4
烏龜跑了5步
main==>5
View Code

3.2  實現 Runnable 接口

/**
 * 1.實現 Runnable 接口 + 重寫 run();  -->真實角色
 * 2.啟動多線程,使用靜態代理 Thread
 *   1)創建真實角色
 *   2)創建代理角色+真實角色引用
 *   3)調用.start();
 * 3.推薦通過實現 Runnable 接口創建線程:
 *   1)避免單繼承的局限性
 *   2)便於共享資源
 */

//真實角色
class NewThread implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<6;i++){
            System.out.println("真實角色==>"+i);
        }    
    }
}
public class Test {
    public static void main(String[] args) {
        //1.創建真實角色
        NewThread t = new NewThread();
        //2.創建靜態代理+真實角色引用
        Thread proxy = new Thread(t);
        //3.調用.start(); 啟動線程
        proxy.start();
        
        for(int i=0;i<6;i++){
            System.out.println("main==>"+i);
        }
    }
}
// 運行結果,每一次運行結果是不同的
main==>0
真實角色==>0
main==>1
真實角色==>1
main==>2
真實角色==>2
main==>3
真實角色==>3
main==>4
真實角色==>4
真實角色==>5
main==>5
View Code

3.3  有返回值的線程,通過 CallableFuture 創建

/**
 *  FutureTask 是 RunnableFuture 接口的實現類(RunnableFuture 繼承於 Runnable 接口,Future 接口)
 *  1. 創建 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將作為線程執行體,並且有返回值
 *  2. 創建 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值
 *  3. 使用 FutureTask 對象作為 Thread 對象的 target 創建並啟動新線程
 *  4. 調用 FutureTask 對象的 get() 方法來獲得子線程執行結束后的返回值
 */
public class CallableTest implements Callable<Integer>{
    @Override  
    public Integer call() throws Exception {  
        int i = 0;  
        for(; i<10; i++){ 
            System.out.println(Thread.currentThread().getName()+"==>"+i);  
        }
        return i;  
    }  
}

public class Test {
    public static void main(String[] args) throws InterruptedException, ExecutionException  {  
        CallableTest callable = new CallableTest();  
        FutureTask<Integer> ft = new FutureTask<>(callable); 
        for(int i=0; i<10; i++){  
            System.out.println(Thread.currentThread().getName()+"==>"+i); 
            if(i==2){
                new Thread(ft, "Callable 線程").start();
            }
        }  
        System.out.println("Callable 線程返回值:" + ft.get());
    }
}
// 運行結果不唯一
main==>0
main==>1
main==>2
main==>3
main==>4
main==>5
Callable 線程==>0
main==>6
main==>7
Callable 線程==>1
main==>8
Callable 線程==>2
Callable 線程==>3
Callable 線程==>4
Callable 線程==>5
main==>9
Callable 線程==>6
Callable 線程==>7
Callable 線程==>8
Callable 線程==>9
Callable 線程返回值:10
View Code
public class Test {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //創建線程池
        ExecutorService pool = Executors.newFixedThreadPool(2);
        CallableTest callable = new CallableTest();
        Future<Integer> f = null;
        for(int i=0; i<100; i++){
            System.out.println("Main==>"+i);
            if(i==10){
                f = pool.submit(callable);
                //關閉線程池
                pool.shutdownNow();
            }
        }
        //獲取返回值
        int num = f.get();
        System.out.println("返回值為:"+num);
    }
}

class CallableTest implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        int i;
        for(i=0; i<100; i++){
            System.out.println(i);
        }
        return i;    
    }
}
View Code

 

4.  Thread 類

// 創建
Thread thread = New Thread();
Thread thread = New Thread(String name);
Thread thread = New Thread(Runnable target);
Thread thread = New Thread(Runnable target, String name);

// 動態方法
thread.isAlive();// 測試線程是否處於活動狀態
thread.start();// 開始執行線程,JVM 調用該線程的 run 方法
thread.setName(String name);// 改變線程名稱
thread.getName();// 獲取線程名稱
thread.setPriority(int priority);// 更改線程的優先級
/* 具有較高優先級的線程應該在低優先級的線程之前分配處理器時間,然而,線程優先級不能保證線程執行的順序,而且非常依賴於平台
   Thread.MIN_PRIORITY    1
   Thread.NORM_PRIORITY   5(默認)
   Thread.MAX_PRIORITY    10     */
thread.getPriority();// 獲取線程的優先級
thread.setDaemon(boolean on);// 將該線程標記為守護線程。守護線程是服務提供者線程,當 JVM 檢測到應用程序中的所有線程都只是守護線程時,它將退出應用程序
thread.isDaemon();// 測試是否為守護線程
thread.join(long millisec);// 等待該線程終止的時間。假設有兩個線程 t1 和 t2,如果線程 t1 調用 t2.join(),線程 t1 開始等待,直到線程 t2 終止,t1 才會繼續執行
thread.interrupt();// 嘗試中斷線程

// 靜態方法
Thread.currentThread();// 返回對當前正在執行的線程對象的引用
Thread.yield();// 嘗試暫停當前正在執行的線程對象,並執行其他線程。該線程放棄當前對處理器的使用 
Thread.sleep(long millisec);// 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。如果線程在進入休眠之前擁有鎖的所有權,則它在休眠期間繼續保持這些鎖
Thread.interrupted();// 測試線程是否中斷
Thread.holdsLock(Object o);// 返回 boolean 類型,當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true
Thread.dumpStack();// 將當前線程的堆棧跟蹤打印至標准錯誤流

 

5.  停止線程

/**
 * 停止線程
 * 1.自然終止:線程體正常執行完畢
 * 2.外部干涉:
 *   1)在線程類中定義線程體使用的標識,關鍵字 volatile
 *   2)線程體使用的標識
 *   3)提供對外的方法改變該標識
 *   4)外部根據條件調用改變標識的方法即可
*/

public
class Study implements Runnable{ //1.線程類中定義線程體使用的標識,關鍵字 volatile 可以保持線程的工作內存中的變量值與它們在主存儲器中的值同步 private volatile boolean flag = true;

@Override
public void run() { //2.在線程體使用標識 while(flag){ System.out.println("study thread......"); } } //3.提供對外的方法改變標識 public void stop(){ this.flag = false; } }
public class Test { public static void main(String[] args) { Study sd = new Study(); new Thread(sd).start(); //4.外部根據條件調用改變標識的方法即可 for(int i=0;i<100;i++){ if(i==50){ sd.stop(); } System.out.println("main==>"+i); } } }

 

6.  線程阻塞

// 線程阻塞1:thread.join();假設有兩個線程 t1 和 t2,如果線程 t1 調用 t2.join(),線程 t1 開始等待,直到線程 t2 終止,t1 才會繼續執行
public class Test01 extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Test01 demo = new Test01();
        Thread t = new Thread(demo);
        t.start();
        
        for(int i=0;i<100;i++){
            t.join();// main 阻塞
            System.out.println("main==>"+i);
        }
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("thread...."+i);
        }
    } 
}

// 線程阻塞2:Thread.yield();暫停當前正在執行的線程對象,並執行其他線程。該線程放棄當前對處理器的使用 
public class Test02 extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Test02 demo = new Test02();
        Thread t = new Thread(demo);
        t.start();
        
        for(int i=0;i<100;i++){
            Thread.yield();// main 阻塞
            System.out.println("main==>"+i);
        }
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("thread...."+i);
        }
    } 
}

// 線程阻塞3:Thread.sleep();
public class Test03 extends Thread {
    public static void main(String[] args) throws InterruptedException {
        Test03 demo = new Test03();
        Thread t = new Thread(demo);
        t.start();
        
        for(int i=0;i<100;i++){
            Thread.sleep(1000);// main 阻塞
            System.out.println("main==>"+i);
        }
    }

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("thread...."+i);
        }
    } 
}

 

7.  synchronized 同步

同步(synchronized),確保資源安全 --> 線程安全,效率低
1. 線程安全:就是多線程訪問時,采用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,直到該線程讀取完,其他線程才可使用,因而不會出現數據不一致或者數據污染
2. 線程不安全:就是不提供數據訪問保護,有可能出現多個線程先后更改數據造成所得到的數據是臟數據
3. 同步塊:synchronized(引用類型 | this | 類名.class){}
4. 同步方法:修飾符 + synchronized + 返回類型 + 方法名(){}
5. 過多的同步方法可能造成死鎖

public class Role implements Runnable{
    private volatile boolean flag = true;
    private int num = 100;
    
    @Override
    public void run() {
        while(flag){
            t1();
        }
    }

    // 同步塊,線程安全,效率低
    public void t1(){
        synchronized(Role.class){
            if(num<=0){
                flag = false;
                return;
            }
            try {
                Thread.sleep(60);// 模擬延遲
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
        }
    }
    
    // 同步方法,線程安全,效率低
    public synchronized void t2(){    
        if(num<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(60);// 模擬延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
    }
    
    // 線程不安全,效率高
    public void t3(){
        if(num<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(60);// 模擬延遲
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"搶到了"+num--);
    } 
}

public class Test {       
    public static void main(String[] args) {
        //1.創建真實角色
        Role r = new Role();
        //2.創建代理,引用真實角色
        Thread td1 = new Thread(r,"甲");
        Thread td2 = new Thread(r,"乙");
        Thread td3 = new Thread(r,"丙");
        //3.啟動線程
        td1.start();
        td2.start();
        td3.start();
    } 
}

 

8.  ThreadGroup 線程組

/**
 * 1. 線程總是線程組的成員
 * 2. 線程組以樹狀結構布置
 * 3. 線程組可以包含另一個線程組
 */

// 創建
ThreadGroup group = new ThreadGroup();// 默認 this.name = "system"
ThreadGroup group = new ThreadGroup(String name);
ThreadGroup group = new ThreadGroup(ThreadGroup parent, String name);

// 添加新線程
Thread  thread = new Thread(ThreadGroup group, String name);
Thread  thread = new Thread(ThreadGroup group, Runnable target);
Thread  thread = new Thread(ThreadGroup group, Runnable target, String name);

// 常用方法
thread.getThreadGroup();// 返回該線程的 ThreadGroup 的引用
group.getName();// 返回該線程組的名稱
group.getParent();// 返回該線程組的父線程組,頂層線程組的父級為 null
group.activeCount();// 返回該線程組中活動線程數的估計值
group.interrupt();// 中斷該線程組的所有線程
group.enumerate(Thread list[]);// 將此線程組及其子組中的每個活動線程復制到指定的數組中,並返回放入數組的線程數

 

9.  線程池

/**
  * 1. 背景
  *  在面向對象編程中,創建和銷毀對象是很費時間的,因為創建一個對象要獲取內存資源或者其它更多資源,
  *  在 Java 中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷毀后進行垃圾回收,
  *  所以提高服務程序效率的一個手段就是盡可能減少創建和銷毀對象的次數,這就是"池化資源"技術的產生原因。
* 2. 線程池 * 1)線程池中維護着多個線程,等待着監督管理者分配可並發執行的任務,這避免了在處理短時間任務時創建與銷毀線程的代價 * 2)任務調度以執行線程的常見方法是使用同步隊列,稱作任務隊列。池中的線程等待隊列中的任務,並把執行完的任務放入完成隊列中 * 3)如果線程池中所有的線程都始終保持繁忙,但隊列中包含掛起的任務,則線程池將在一段時間后創建另一個輔助線程(但線程的數目永遠不會超過最大值,超過最大值的線程可以排隊,但他們要等到其他線程完成后才啟動)
* 3. 針對線程池,Java 提供了以下類: * 1)java.util.concurrent.Executor 接口,只提供了一個抽象方法:void execute(Runnable command); * 2)java.util.concurrent.Executors 工具類 * 3)java.util.concurrent.ExecutorService 接口,繼承於 Executor 接口 * 4)java.util.concurrent.AbstractExecutorService 抽象類,是 ExecutorService 接口的實現類 * 5)java.util.concurrent.ThreadPoolExecutor 類,繼承於 AbstractExecutorService 抽象類 */
// 創建 ExecutorService pool = Executors.newFixedThreadPool(int nThreads);// nThreads 線程池中允許的最大線程數

// 常用方法 pool.execute(Runnable<T> task);// 在將來的某個時候執行給定的任務 pool.submit(Runnable<T> task);// 提交要執行的任務,並返回表示該任務的 Future,在成功完成任務后 Future 的 get 方法將返回 null pool.submit(Callable<T> task); pool.awaitTermination(long timeout, TimeUnit unit);// 等待終止的最大時間;timeout 等待的最長時間;unit 時間單位;如果執行器終止返回 true,超時返回 false,如果在等待過程中被中斷,則拋出 InterruptedException pool.isTerminated(); pool.shutdown();// 啟動有序關閉,在此過程中執行先前提交的任務,但不接受任何新任務;無返回值 pool.shutdownNow();// 嘗試停止所有正在積極執行的任務,停止處理等待的任務,並返回等待執行的任務列表 List<Runnable>
pool.isShutdown();

 


免責聲明!

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



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