java多線程以及線程池技術總結

線程
1.1 基本概念

程序 - 存放在硬盤/磁盤上的可執行文件。
進程 - 運行在內存中的程序。

操作系統中採用時間片輪轉法來保證多個進程/線程併發執行,所謂的併發就是宏觀並行,微觀串行。

目前主流的操作系統都支持多進程,可以讓操作系統同時執行多個任務,進程是重量級的,新建進程對系統的資源消耗比較大。
爲了避免資源消耗過大,引出線程的概念。
線程是進程內部的程序流,共享所在進程的系統資源,通俗來說,操作系統支持多進程,而每一個進程支持多線程。
以後的主流的開發都採用多線程技術。

1.2 線程的創建

(1)Thread類的基本概念
java.lang.Thread類用於創建線程對象,java虛擬機允許同時運行多個線程。
創建和啓動線程的主要方式:
a.自定義類繼承自Thread類,並重寫run()方法,調用start()方法啓動線程。
b.自定義類實現Runnable接口,並重寫run()方法,調用start()方法啓動線程。

(2)Thread類的常用方法
Thread() - 使用無參的形式構造線程對象。
Thread(String name) - 使用參數指定的名稱來構造線程對象。 Thread(Runnable target) - 根據參數指定的接口引用來構造線程對象。
Thread(Runnable target, String name) - 根據接口引用和線程名稱共同構造對象。

void run() - 若使用Runnable接口作爲參數創建了線程對象,則調用該接口中被重寫以後的run()方法;否則該方法什麼也不做就直接返回。 void start() - 使得線程啓動起來,java虛擬機會自動調用該線程的run()方法。
(3)線程的原理分析
執行main()方法的線程叫做主線程,調用start()方法出現出來的線程叫做新/子線程
對於start()方法之前的代碼來說,只會被主線程自己執行一遍,而start()方法一旦調用成功,該進程中的線程瞬間由1個變成了2個,其中主線程繼續執行start()方法之後的語句塊,而新啓動的子線程去執行run()方法中的語句塊。

當run()方法的方法體執行完畢時,子線程結束;當main()方法的方法體執行完畢時,主線程結束,主線程和子線程各自獨立運行,沒有執行的先後次序,取決於操作系統的調度算法。
經驗:
目前創建線程的方式有兩種:第一種繼承的方式代碼相對簡單但不支持多繼承;第二種實現接口的方式代碼相對複雜但可以多實現並單繼承,因此以後開發中推薦使用第二種方式。

1.3 線程的名稱和編號

long getId() - 用於返回當前線程的標識符/編號。
String getName() - 用於返回當前線程的名稱。
void setName(String name) - 用於修改當前線程的名稱。
static Thread currentThread() - 用於返回當前正在執行的線程對象的引用。

1.4 線程的主要狀態

新建狀態—就緒狀態—運行狀態(或阻塞狀態)—消亡狀態


新建狀態 - 當線程對象剛被創建出來的狀態。
- 此時線程並沒有開始執行。 就緒狀態 - 當線程調用start()方法之後進入的狀態。
- 此時線程還是沒有開始執行。 運行狀態 - 當線程調用調度器調度就緒狀態的線程之後的狀態
- 當時間片執行完畢但任務沒有完成時回到就緒狀態 消亡狀態 - 當線程的任務已經完成時進入的狀態。 阻塞狀態 - 當線程的執行過程中發生了阻塞事件(sleep)進入的狀態。
- 當阻塞狀態解除之後進入就緒狀態。

1.5 線程的基本操作

static void yield()
- 用於暫停當前正在執行的線程,轉而執行其他線程。
- 讓出CPU的執行權,回到就緒狀態。
static void sleep(long millis)- 讓當前正在執行的線程休眠參數指定的毫秒。
void interrupt() - 中斷線程,通常用於打斷睡眠。
void join() - 等待當前線程終止,沒有時間的限制。
void join(long millis) -最多等待當前線程參數指定的毫秒。
void setPriority(int newPriority) - 更改線程的優先級。
int getPriority() - 獲取線程的優先級。
- 優先級最高的線程不一定最先執行,只是獲取時間片的機會更多一些而已。

重點:
sleep() 和 join()

2.線程的同步

StringBuilder類 - 後期增加的類,非線程安全的類,效率比較高。
- 不支持線程的同步技術。
StringBuffer類 - 早期就有的類,線程安全的類,效率比較低。
- 支持線程的同步技術。

2.1 基本概念

當多個線程在同一時刻訪問同一種共享資源時,可能會造成數據的不一致等問題,爲了避免該問題的發生,就需要對線程之間進行協調和通信,而線程之間的協調和通信就是線程同步機制。

2.2 解決方案

只需要將線程的並行改爲串行就可以解決該問題。
此時可以解決問題,但是效率相對比較低,因此建議能不用則不用。

2.3 實現方法

爲了實現線程的同步,需要藉助java中synchronized關鍵字來保證原子性,具體實現方式如下:
(1)使用synchronized關鍵字修飾整個方法,語法格式:
訪問修飾符 synchronized 返回值類型 方法名(形參列表){}
(2)使用synchronized關鍵字修飾方法中的部分代碼,語法格式:
synchronized(this){
寫入需要被鎖定的語句塊;
}
死鎖:兩個線程或多個線程相互鎖定時
避免方式:順序上鎖,反向解鎖

2.4 原理分析

當多個線程同時執行同一個方法時,爲了避免線程之間的衝突問題,通常都會給該方法加上一把同步鎖,當有線程先搶到同步鎖時就可以進入同步語句塊執行,其他線程只能進入阻塞狀態,當該線程執行完畢同步語句塊後會自動釋放同步鎖,阻塞的線程又可以搶佔同步鎖,搶佔成功的線程去執行同步語句塊,搶佔不成功的線程繼續阻塞。

線程和進程有什麼區別?

一個進程是一個獨立(self
contained)的運行環境,它可以被看作一個程序或者一個應用。而線程是在進程中執行的一個任務。線程是進程的子集,一個進程可以有很多線程,每條線程並行執行不同的任務。不同的進程使用不同的內存空間,而所有的線程共享一片相同的內存空間。

線程池(集中管理多線程的)

線程池是指在初始化一個多線程應用程序過程中創建一個線程集合,然後在需要執行新的任務時重用這些線程而不是新建一個線程。線程池中的每個線程都有被分配一個任務,一旦任務已經完成了,線程回到池子中並等待下一次分配任務。

影響一個多線程應用程序的相應時間的幾個主要因素之一是爲每個任務生成一個線程的時間。

線程池的組成

1、線程池管理器(ThreadPoolManager):用於創建並管理線程池
2、工作線程(WorkThread): 線程池中線程
3、任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行。
4、任務隊列:用於存放沒有處理的任務。提供一種緩衝機制。
線程池的優點
1)避免線程的創建和銷燬帶來的性能開銷。
2)避免大量的線程間因互相搶佔系統資源導致的阻塞現象。
3}能夠對線程進行簡單的管理並提供定時執行、間隔執行等功能。

代碼部分

啓動和創建線程的兩種方式:
//繼承自Thread類並重寫run()方法,調用start()方法啓動線程.
1.繼承java.lang.Thread
2.public class SubThread extends Thread {
    @Override
    public void run() {
    sys..................
    }
    public static void main(String[] args) {
    Thread t1 = new 類名();
    t1.stack();
    sys..................
    }
}
//自定義實現Runnable接口,並重寫run()方法,調用start()方法啓動線程.
1.實現Runnable接口
2.public class SubRunnable(類名) implements Runnable{
    @Override
    public void run(){
        system.......................;
    }
    public static void main(String[] args){
        Thread t1 = new Thread(new SubRunnable());
        t1.start();
        system.........................;

    }
}


----------


//匿名內部類
public class TestNoNameStrat extends Thread {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                System.out.println("左手星辰雨下");
            }
        }.start();
        new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("莫顏世界");
            }
        }).start();
    }
}


----------


//子線程、主線程的編號
public class TestNames extends Thread {
    public TestNames(String name){
        super(name);

    }
    public void run(){
        System.out.println("子線程"+getId()+"姓名"+getName());
        this.setName("諸葛亮");
        System.out.println("子線程"+getId()+"姓名"+getName());
    }
    public static void main(String[] args) {
        Thread t1 = new TestNames("劉備");
        t1.start();
        Thread t =Thread.currentThread();
        System.out.println("主線程"+t.getId()+"姓名"+t.getName());
    }

}


----------


public class TestNoNameStrat extends Thread {
public static void main(String[] args) {

        //1.將繼承的方式改爲匿名內部類的策略實現
        new Thread(){
            @Override
            public void run(){
                System.out.println("世界上最癡情的等待,就是你"
                        + "在switch我在case,也許永遠都不會選擇我!");
            }
        }.start();

//2.將接口的方式改爲匿名內部類的策略實現
new Thread(new Runnable(){
    @Override
    public void run() {
        System.out.println("世界上最真情的相依,就是你在try我在catch"+ ",無論你發神馬脾氣,我都靜靜地處理!");
            }
        }).start();
    }
}


----------


//模擬ATM去後臺服務器讀取賬戶餘額的過程
public class Account implements Runnable {

    private int money = 1000; //賬戶餘額

    @Override
    public /*synchronized*/ void run() {
        synchronized(this){
            //模擬ATM去後臺服務器讀取賬戶餘額的過程
            int temp = money;
            //判斷賬戶餘額是否>=200元
            if(temp >= 200){
                //模擬取款的過程
                System.out.println("正在出鈔,請稍後...");
                temp -= 200; // temp = temp - 200; 800 800
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("請取走您的鈔票!");
            }
            else{
                System.out.println("賬戶餘額不足,請重新覈對金額!");
            }
            //將ATM中處理之後的賬戶餘額重寫寫入後臺的服務器
            money = temp;  //800 800
        }
    }

    public static void main(String[] args) {
        Account acc = new Account();
        Thread t1 = new Thread(acc);
        Thread t2 = new Thread(acc);
        t1.start();
        t2.start();
        //讓主線程等待子線程終止,然後打印賬戶餘額
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("最後的賬戶餘額是:" + acc.money);//800
    }

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