首頁 > Java > Java基礎 > 主體

Java講解ThreadPool執行緒池

coldplay.xixi
發布: 2021-03-03 10:36:31
轉載
1775 人瀏覽過

Java講解ThreadPool執行緒池

ThreadPool執行緒池

  • 1.執行緒池的優點
    • ##1.1.引言
    • 1.2.為什麼要使用執行緒池
  • 2.執行緒池的使用
    • 2.1.架構說明
    • #2.2.執行緒池的三大方法
      • 2.2.1.newFixedThreadPool(int)方法
      • 2.2.2.newSingleThreadExector
      • # 2.2.3.newCachedThreadPool
  • #3.ThreadPoolExecutor底層原理
  • 4.執行緒池7大重要參數
#(相關免費學習推薦:

java基礎教學

1.線程池的優點

1.1.引言

與資料庫執行緒池類似,如果沒有資料庫連線池,那麼每次對資料庫的連線池都要new來取得連線池。重複的連線和釋放作業會消耗大量的系統資源,我們可以使用資料庫連線池,直接去池中取連結池。

同樣,在沒有線程池之前,我們也是透過new Thread.start()來取得線程,現在我們也不需要new了,這樣就能實現復用,使得我們系統變得更有效率。

1.2.為什麼要使用線程池

範例:

    10年前單核心CPU電腦,假的多線程,像馬戲團小丑玩多個球,CPU需要來回切換。
  • 現在是多核心電腦,多個執行緒各自跑在獨立的CPU上,不用切換效率高。

執行緒池的優點:

執行緒池所做的工作只要是控制執行的執行緒數量,處理過程中將任務放入佇列,然後在執行緒建立後啟動這些任務,如果執行緒數量超過了最大數量,超出數量的執行緒排隊等候,等其他執行緒執行完畢,再從佇列中取出任務來執行。

它的主要特點是:

  • 執行緒複用
  • 控制最大並發數字
  • 管理執行緒
優點:

  • #第一:降低資源消耗。透過重複利用已建立的執行緒來降低執行緒建立和銷毀造成的銷耗。
  • 第二:提高反應速度。當任務到達時,任務可以不需要等待執行緒建立就能立即執行。
  • 第三:提高執行緒的可管理性。執行緒是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用執行緒池可以進行統一的分配,調優和監控。

2.執行緒池的使用

#2.1.架構說明

Executor框架是什麼?

Java Doc中是這麼描述的

An object that executes submitted Runnable tasks. This interface provides a way of decoupling task submission from the mechanics of how each task will be run, including details of thread use, scheduling, etc. An Executor is normally used instead of explicitly creating threads.

#執行提交的Runnable任務的物件。這個介面提供了一種將任務提交與如何運行每個任務的機制,包括執行緒的詳細資訊使用、調度等。通常使用Executor而不是明確地建立線程。

Java中的執行緒池是透過Executor框架實現的,,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個類別。

而我們常用的介面是ExecutorService子介面Executors是線程的工具類別(類似陣列的工具類別Arrays,集合的工具類別Collections)ThreadPoolExecutor是這些類別的重點我們可以透過輔助工具類別Executors拿到ThreadPoolExecutor執行緒池
Java講解ThreadPool執行緒池 各個類別更詳細的介紹如下:

Executor所有執行緒池的接口,只有一個方法,該介面定義執行Runnable任務的方式
ExecutorService 增加Executor的行為,是Executor實作類別的最直接的介面,該介面定義提供對Executor的服務
#Executors 線程池工廠類別,提供了一系列工廠方法用於創建線程池,返回的線程池都實現了 ScheduledExecutorService:定時調度介面。
AbstractExecutorService 執行框架抽象類別。

ThreadPoolExecutor JDK中線程池的具體實作,一般用的各種線程池都是基於這個類別實現的

2.2.線程池的三大方法

2.2.1.newFixedThreadPool(int)方法

Exectors.newFixedThreadPool(int) -->

執行長期任務效能好,建立一個線程池,一池有N個固定的線程,有固定線程數的線程#

	public static void main(String[] args) {
		//一池5个受理线程,类似一个银行5个受理窗口。不管你现在多少个线程,都只有5个
		ExecutorService threadPool=Executors.newFixedThreadPool(5); 
		
		try {
			//模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务。
			for(int i=1;i{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}
登入後複製

Java講解ThreadPool執行緒池
可以看到執行結果。池子中有5個線程,相當於5個工作人員對外提供服務,辦理業務。圖中1號窗口辦理了兩次業務,銀行的受理窗口可以多次重複使用。也不一定是每個人辦理兩次,而是誰辦理的快誰就辦理的多。

當我們再執行緒執行的過程中加400ms的延遲,可以看看效果

public static void main(String[] args) {
		//一池5个受理线程,类似一个银行5个受理窗口。不管你现在多少个线程,都只有5个
		ExecutorService threadPool=Executors.newFixedThreadPool(5); 
		
		try {
			//模拟有10个顾客过来银行办理业务,目前池子里面有5个工作人员提供服务。
			for(int i=1;i{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
				try {
					TimeUnit.MILLISECONDS.sleep(400);
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}
登入後複製

Java講解ThreadPool執行緒池
此時說明網路擁塞的情況下或是辦理業務比較慢,則執行緒池辦理業務任務分配情況比較平均。

2.2.2.newSingleThreadExector

Exectors.newSingleThreadExector()–>一個任務一個任務的執行,一池一執行緒

public static void main(String[] args) {
	//一池一个工作线程,类似一个银行有1个受理窗口
	ExecutorService threadPool=Executors.newSingleThreadExecutor(); 	
	try {
		//模拟有10个顾客过来银行办理业务
		for(int i=1;i{
				System.out.println(Thread.currentThread().getName()+"\t 办理业务");
			});
		}
	} catch (Exception e) {
		// TODO: handle exception
	}finally{
		threadPool.shutdown();
	}	}
登入後複製

Java講解ThreadPool執行緒池

2.2.3.newCachedThreadPool

Exectors.newCachedThreadPool()–>執行許多短期非同步任務,執行緒池根據需要建立新線程,但在先前建立的線程可用時將重複使用他們。可擴容,遇強則強。一池n線程,可擴容,可伸縮,cache緩存的意思
那麼池的數量應該設置多少呢,如果銀行只有一個窗口,那麼當人來得太多了,就忙不過來。如果銀行有很多個窗口,但是人來的少,此時又顯得浪費資源。那麼該如何該合理安排呢?這就需要用到newCachedThreadPool()方法,可擴容,可伸縮

public static void main(String[] args) {
		//一池一个工作线程,类似一个银行有n个受理窗口
		ExecutorService threadPool=Executors.newCachedThreadPool(); 	
		try {
			//模拟有10个顾客过来银行办理业务
			for(int i=1;i{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}
登入後複製
登入後複製

Java講解ThreadPool執行緒池

public static void main(String[] args) {
		//一池一个工作线程,类似一个银行有n个受理窗口
		ExecutorService threadPool=Executors.newCachedThreadPool(); 	
		try {
			//模拟有10个顾客过来银行办理业务
			for(int i=1;i{
					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
				});
			}
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			threadPool.shutdown();
		}	
	}
登入後複製
登入後複製

Java講解ThreadPool執行緒池

#3.ThreadPoolExecutor底層原理

newFixedThreadPool底層原始碼

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<runnable>());
    }</runnable>
登入後複製

可以看到,底層的參數包含LinkedBlockingQueue阻塞佇列。

newSingleThreadExecutor底層原始碼

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<runnable>()));
    }</runnable>
登入後複製

newCachedThreadPool底層原始碼

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<runnable>());
    }</runnable>
登入後複製

SynchronousQueue這個阻塞佇列是單一版阻塞佇列,阻塞佇列的容量為1.

這3個方法其實都共同回傳了一個對象,也就是ThreadPoolExecutor的物件

4.線程池7大重要參數

ThreadPoolExecutor的建構子

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize <p>上面的int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit ,BlockingQueue workQueue,ThreadFactory threadFactory,<br> RejectedExecutionHandler handler即我們的七大執行緒參數<br> 上面是ThreadPoolExecutor類別的建構方法,有7大參數:</p>
<p>#1)<strong>corePoolSize:線程池中的常駐核心執行緒數</strong>,簡稱核心數。 <br> 比如說,一個線程池我們可以把它當作銀行的網點,銀行只要開門,就必須至少有一個人在值班,這個就叫常駐核心線程數。例如如果某個銀行週一到週五五個網點全開,那麼週一到週五的常駐核心線程數為5.如果今天業務沒有那麼頻繁,窗口為1,那麼今天的常駐核心線程數就是1 </p>
<p>2)<strong>maxImumPoolSize:在執行緒池中能夠容納同時執行的最大執行緒數,此值必須大於等於1</strong></p>
<p>3)<strong>keepAliveTime:多餘的空閒執行緒的存活時間,目前池中執行緒數超過corePoolSize時,當空閒時間達到keepAliveTime時,多餘執行緒會被銷毀直到剩下corePoolSize為止</strong><br> 如果執行緒池中有常駐執行緒數,又有最大線程數,說明平常是用常駐的,工作緊張了,它會擴容到最大線程數,如果業務降下來了,我們設置了多餘的空閒線程的存活時間,比如設置30s,如果30s都沒有多餘的請求過來,有些銀行就會關閉窗口,所以它不僅會擴大還會縮小。 </p>
<p>4)<strong>unit:keepAliveTime的單位</strong><br> 單位:是秒,毫秒,微秒。 </p>
<p><strong>5)workQueue:任務隊列,被提交但尚未被執行的任務</strong><br> 這是一個阻塞隊列,比如說銀行,只有3個受理窗口,而來了4個客戶。這個阻塞隊列就是銀行的候客區,來了客戶不能讓他走了。視窗數控制了執行緒的並發數。 </p>
<p>6)<strong>threadFactory:表示產生線程池中工作線程的線程工廠,用於創建線程,一般預設即可</strong><br> 線程都是統一的創建。線程池裡面有已經new好的線程,這些由線程池工廠生產。 </p>
<p><strong>7)handler:拒絕策略,表示目前佇列滿了,且工作執行緒大於等於執行緒池的最大執行緒數(maximumPoolSize)時如何來拒絕請求執行的runnable的策略</strong></p>
<p>比如說今天銀行客流高峰,三個窗口都滿了,候客區也滿了。我們沒有選擇繼續拉人,因為不安全我們選擇委婉的拒絕。 </p>
<p>在下一節我們將介紹線程池底層工作原理</p>
<blockquote><p><strong>相關學習推薦:</strong><a href="https://www.php.cn/java/base/" target="_blank"><strong>java基礎</strong></a></p></blockquote>#</runnable>
登入後複製

以上是Java講解ThreadPool執行緒池的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!