中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java線程的原理和實現方式

發布時間:2021-06-26 14:49:09 來源:億速云 閱讀:141 作者:chen 欄目:大數據

本篇內容介紹了“java線程的原理和實現方式”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

多線程的實現方式以及區別

我們都知道,實現多線程的方式是繼承Thread類和實現Runable接口,那么除了java中不允許多繼承的這個特性,他們之間還有什么區別呢?我們看下面這個例子:

//繼承Thread類
public class ThreadTest extends Thread {
    private AtomicInteger count = new AtomicInteger(0);
    @Override
    public void run() {
       for(int i=0;i<5;i++){
           System.out.println(Thread.currentThread().getName() + " " + count.incrementAndGet());
       }
    }

    public static void main(String[] args) {
        new ThreadTest().start();
        new ThreadTest().start();
    }
}
//實現Runable接口
public class RunnableTest implements Runnable {
    private AtomicInteger count = new AtomicInteger(0);
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " " + count.incrementAndGet());
        }
    }

    public static void main(String[] args) {
        RunnableTest rab=new RunnableTest();
        new Thread(rab).start();
        new Thread(rab).start();
    }
}

通過觀察console信息我們可以看到,繼承Thread類,無法共享對象資源,而實現Runable則可以;這讓我們可以針對不同的業務情況作出不同的選擇。
java線程的原理和實現方式

線程的狀態

Thread.State枚舉中定義了線程的六種狀態

狀態說明
NEWnew 對象后線程狀態
RUNNABLEstart方法調用后;或者waiting狀態下的線程調用了notify、LockSupport.Unpark等方法
BLOCKED等待synchronized代碼塊時的狀態
WAITING調用wait()、join(),LockSupport.park()方法后
TIMED_WAITING調用wait(long)、sleep(long),join(long)方法后
TERMINATED線程運行完畢,或者調用terminate方法成功

關于線程狀態流轉,看下面這幅圖;左邊是正常狀態流轉,右邊是存在block和waiting的情況:
java線程的原理和實現方式

網上有些文章說線程還分為ready和running狀態,這里需要注意的是,這兩種執行狀態都屬于Runable狀態;ready和running狀態其實是描述VM/OS是否線程分配CPU資源,JVM并不能決定這一事情。當然運行中的線程通過調用yield方法是可以將running狀態的線程切換到ready狀態,但這一過程是OS層面的事情而非JVM層面。

有關線程的方法

在實現多線程的過程中,我們需要用到相關方法來進行線程狀態的切換,已達到不同的目的。

  • Thread類的相關方法

方法說明
start啟動線程,使線程進入運行狀態
setPriority給線程設置優先級,有三個可選項:Thread.MIN_PRIORITY 最低優先級、Thread.NORM_PRIORITY 普通、Thread.MAX_PRIORITY 最高
sleep使線程進入睡眠狀態,需要指定時間,線程進入等待狀態,此時線程仍然持有鎖
yield使線程交出CPU資源給優先級是同級及以上的線程,不可以指定時間,線程仍然是RUNABLE狀態,此時線程仍然持有鎖
interrupt終止未執行的線程,正在執行的線程不受影響,被終止的線程進入TERMINATED終止狀態
stop強行終止線程,不管線程是否在執行中,此方法為廢棄方法,不是線程安全方法,因為會釋放線程持有的鎖導致數據不一致的問題產生
join等待線程執行完畢,一般是在主線程中等待子線程執行結束,此方法會阻塞當前線程
  • Object類的相關方法
    Obj類主要有wait()和notify()、notifyAll()三個方法來控制多線程讀共享數據的訪問;注意這三個方法不是Thread類的,故只能在同步代碼塊中才會產生效果。

方法說明
wait使線程暫停,并釋放持有的鎖,需要手動調用notify方法喚醒;線程暫停后會放入等待隊列
wait(long)同wait,但是可以指定暫停的時間
notify喚醒暫停的線程,將線程移出等待隊列;線程喚醒后并不立即執行,而是放入獲取鎖的隊列中等待獲取鎖
notifyAll喚醒所有暫停的線程,將所有線程沖等待隊列中移出,并放入獲取鎖的隊列中

線程池

我們在系統中直接new線程是可以實現多線程,但是存在以下弊端:

  • 線程不能重用,過多的線程創建和銷毀會降低系統性能

  • 不能控制線程并發數量

  • 不能指定線程的定時執行等

ThreadPoolExecutor

ThreadPoolExecutor讓我們可以自定義線程池,它有三個構造函數,它的參數含義如下 :
java線程的原理和實現方式

參數說明
int corePoolSize核心線程數量,當線程池內的線程數小于corePoolSize,會新建線程馬上運行任務;這些線程創建后不會進行回收操作,即便是閑置狀態
int maximumPoolSize最大線程數量,此參數需大于corePoolSize;當添加任務時,如果當前線程池線程數量大于corePoolSize,會提交給等待隊列,當隊列滿了后,會新建線程(運行線程數不超過maximumPoolSize情況下),這一部分線程我們稱之為非核心線程數,它和核心線程沒有區別,只是系統會定時回收線程,最終線程數會保持在corePoolSize數量
long keepAliveTime非核心線程的回收時間,默認60
TimeUnit unitkeepAliveTime的單位,默認秒
BlockingQueue<Runnable> workQueue線程池中的任務隊列,提交任務時如果核心線程數達到,則會提交到這里排隊
ThreadFactory threadFactory創建線程的工廠,可以給每個創建出來的線程設置名字。一般情況下無須設置該參數
RejectedExecutionHandler handler拒絕策略,這是當任務隊列和線程池都滿了時所采取的應對策略,默認是AbordPolicy,表示直接拋出RejectedExecutionException 異常
  • 任務隊列

workQueue指明了當核心線程數達到最大時,任務的排隊策略;有三種類型:

參數說明缺點
直接提交例如:SynchronousQueue(默認),這是一個沒有數據緩沖的阻塞隊列,隊列中只能存放一個元素,超出之后后續線程提交會阻塞(maximumPoolSize達到閾值情況下)線程阻塞
無界隊列例如:不設定容量的LinkedBlockingQueue,當核心線程數滿了之后,任務提交后可以一直存放在隊列中maximumPoolSize失效,且有OOM風險
有界隊列例如:ArrayBlockingQueue,當核心線程數滿了之后,一定量的任務提交可以存放在隊列中,但是后續如果添加新的任務,需要有拒絕策略需要指定拒絕策略
  • 拒絕策略

在使用有界隊列的前提下,如果工作隊列已滿,我們需要設定拒絕策略

參數說明
ThreadPoolExecutor.AbortPolicy默認策略;直接拋出RejectedExecutionException異常
ThreadPoolExecutor.CallerRunsPolicy將任務交給主線程執行,通過阻塞主線程達到減緩提交的作用
ThreadPoolExecutor.DiscardPolicy直接丟棄當前任務
ThreadPoolExecutor.DiscardOldestPolicy丟棄最老的任務

以上拒絕策略中,除了CallerRunsPolicy其他的都好理解,下面我們使用CallerRunsPolicy策略來和DiscardPolicy進行一個比對:
java線程的原理和實現方式
我們定義了最大線程是3個,排隊兩個,線程睡眠0.5s以達到效果;使用DiscardPolicy策略我們可以看到10個任務丟棄了5個。
但是CallerRunsPolicy策略會讓主線程來執行任務,同時將主線程阻塞,已達到延緩提交任務的效果;當主線程執行完畢后,線程池內任務也執行完畢了,這時線程池會繼續接受后續任務。

線程池提交方式

我們可以通過submit、execute方法提交任務給線程池執行,其中submit方法有三個重載,他們有以下區別

方法說明是否關心結果
void execute無返回值,提交后任務和主線程再無瓜葛
<T> Future<T> submit(Callable<T> task)返回一個代表執行結果的Future對象,當調用Future的get方法時會獲取到執行結果,如果線程發生異常會獲取到異常信息
Future<?> submit(Runnable task)返回一個代表執行結果的Future對象,,當調用Future的get方法時,成功返回null,如果線程發生異常會獲取到異常信息
<T> Future<T> submit(Runnable task, T result)當線程正常結束的時候調用Future的get方法會返回result對象,當線程拋出異常的時候會獲取到對應的異常的信息

線程池優化策略

  • CPU密集型任務,就需要盡量壓榨CPU,參考值可以設為 NCPU+1

  • IO密集型任務,參考值可以設置為2*NCPU

Executors

上面ThreadExecutorPool提供給我們手動實現線程池的方式,同時J.U.C包下面提供了線程池Executors類,可以讓我們方便的創建線程池。
Executors提供了5種線程池的實現方式,以針對不同的業務場景:
java線程的原理和實現方式

從上圖中我們可以看到,除了newWorkStealingPool以外,其他的線程池都只是通過ThreadExecutorPool構造函數,傳遞不同的參數而實現不同的效果,這也是本篇博客為什么先介紹ThreadExecutorPool的原因;雖然這幾種線程池區別已經一目了然,我們還是列舉一下它們的特點:

方法說明缺點
newCachedThreadPool初始不指定線程池大小,提交任務后就創建線程,每隔60s回收一下空閑線程最大并發數不可控制:導致系統資源耗盡;不拒絕任務:存在OOM風險
newFixedThreadPool指定固定大小線程池,不存在非核心線程,使用無界隊列無界隊列:存在OOM風險
newScheduledThreadPool在手動創建ThreadExecutorPool的基礎上加了一個定期任務,例如給線程池提交了兩個任務,設置10s運行一次這兩個任務需要注意ThreadExecutorPool參數
newSingleThreadExecutor只有一個線程的線程池,使用無界隊列單線程略顯單薄;無界隊列:存在OOM風險
newWorkStealingPool返回一個ForkJoinPool而不是ThreadExecutorPool對象,可以指定線程數,詳情見下面ForkJoinPool描述適用于大任務情況
  • ForkJoinPool
    ForkJoinPool適用于大型任務,其核心是Fork和Join;Fork可以將一個大任務拆分為多個小的任務,Join會將多個小的任務的結果匯總達到最終運行效果。

舉個例子:假設一個線程池中并發數控制在兩個,但是每個任務都要運行1分鐘;假設我們當前服務器有4個CPU,兩個在處理任務,其他的則為空閑狀態,這時剩下的兩個空閑CPU資源是浪費的。
試想一下:如果們能使剩下的兩個空閑的CPU也能利用起來處理上面兩個任務,任務運行肯定會加快,這就是ForkJoinPool的設計出發點。

不要使用Executors

多數情況下,不推薦使用Executors,而推薦手動創建ThreadExecutorPool,因為Executors的五種實現方式有一下缺點:

  • 要么不能控制并發:資源耗盡、OOM

  • 要么不能設置等待隊列大小:OOM

  • 不能指定拒絕策略

線程池的相關方法

方法說明
shutdown關閉線程池,執行以前提交的任務,但是不接受新提交的任務
isTerminated如果調用了shutdown,并且所有任務已經完成,則返回true,否則永遠false
getActiveCount線程池存貨的數量
getQueue().size等待隊列大小
getPoolSize線程池中當前線程數量
getLargestPoolSize曾經有過的最大線程數量
getTaskCount未完成的任務數量
getCompletedTaskCount完成的任務數量

“java線程的原理和實現方式”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

石嘴山市| 渝中区| 桦川县| 沅江市| 鹤庆县| 青田县| 娱乐| 霍山县| 博湖县| 淮安市| 子洲县| 历史| 肇源县| 仙居县| 彭泽县| 双流县| 乌恰县| 乌鲁木齐县| 遂平县| 德安县| 鄂尔多斯市| 岳阳县| 深圳市| 滨州市| 永新县| 昌平区| 宁陕县| 宁津县| 西城区| 贺兰县| 普陀区| 叶城县| 凤阳县| 安平县| 岳阳县| 札达县| 平阴县| 横山县| 宁安市| 河南省| 双峰县|