併發編程之線程池原理深入解析

線程池在代碼中用的還是比較多,不管什麼,都是用的池子,比如JDBC。在做一個生活中的例子吧,比如說你洗菜肯定會給要洗的才放入洗菜盆中來洗(洗菜盆就相當於一個池子),你肯定不會一根一根的洗,浪費水資源不說,最主要的還浪費水資源。

在這裏插入圖片描述

請原諒我是一個平頂山人,我對象都說我很囉嗦,但是我也沒有感覺。
a).什麼是線程池以及它的作用
b).JAVA開發中,合理的使用線程池有三大好處;
c).線程池的分類
e).線程池的原理分析
f).合理的配置線程數數量
------------------------------------------------------華麗的分界線------------------------------------------------------

a).什麼是線程池以及它的作用

JAVA中線程池是運用最多的併發框架,幾乎所有併發或者異步的編程都可以使用線程池。
主要就是通過幾個固定的線程來操作服務,減少了創建和銷燬線程的過程,從而提高效率。

b).JAVA開發中,合理的使用線程池有三大好處;

a).降低消耗資源

通過重複的創建線程以及銷燬線程能夠降低消耗

b).提高響應速度

當 達到任務時候,任務可以不等到線程創建就能執行任務

c).提高線程的可管理性

線程比較稀缺,如果過度的創建線程,不僅不能夠優化代碼,而且還會降低系統的性能。可以使用I線程池統一分配,管理,監控。

c).線程池的分類

前言:JAVA支持併發,但是頻繁的創建線程和銷燬線程會很浪費資源的,並且線程又很寶貴的。在JDK1.5以前的版本中,線程池非常的醜陋的,但是在JDK1.5之後加入了java.uril.concurrent包,對併發有了一個很大的幫助。今天主要說下Executor接口。

d).線程池創建的四種方式

前言:Executor框架最頂層的是ThreadPoolExecutor類,Executors主要提供的有newScheduledThreadPool,newFixedThreadPool,newCachedThreadPool,newSingleThreadExcutor,只是構造方法,參數不同,可以適用不同的場景
第一種:newCachedThreadPool:

創建一個可緩存的線程池,緩存線程池如果超過處理需要,可回收線程。比如說:線程池雖然創建了10個線程,但是運行的線程只有4個,線程用完之後回收,方便其他線程來使用

package com.aaa.demoThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewCachedThreadPoolDemo {
	public static void main(String[] args) {
		//創建的是newCachedThreadPool線程池子
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		for(int i=0;i<30;i++){
			 int temp=i;
			
			cachedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println("名稱:"+Thread.currentThread().getName()+"執行的是:"+temp);
				}
			});
		}
	}

}

結果如圖:
在這裏插入圖片描述

第二種:newFixedThreadPool:

創建一個定長線程池,可控制最大併發數,超出的線程數量會在外邊等待

package com.aaa.demoThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewFixedThreadPool {
	public static void main(String[] args) {
		
		ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
		for (int i = 0; i < 20; i++) {
			int temp=i;
			newFixedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(Thread.currentThread().getName()+"i:"+temp);
				}
			});
		}
		
	}

}

結果如圖所示;
在這裏插入圖片描述

第三種:newScheduledThreadPool:

這個線程池會定時調用方法

package com.aaa.demoThread;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class NewScheduledThreadPool {
	public static void main(String[] args) {
		
		ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
		for (int i = 0; i < 10; i++) {
			int temp= i;
			newScheduledThreadPool.schedule(new Runnable() {
				
				@Override
				public void run() {
					System.out.println("名稱:"+Thread.currentThread().getName()+"執行的是:"+temp);
					
				}
			}, 5, TimeUnit.SECONDS);
		}
	}

}

5s運行結果:
在這裏插入圖片描述

第三種:newSingleThreadPool:

這個是一個單例的線程,實戰中幾乎用到的不多

package com.aaa.demoThread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class newSingleThreadPool {
	public static void main(String[] args) {
		ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
		for (int i = 0; i < 10; i++) {
			final int index = i;
			newSingleThreadExecutor.execute(new Runnable() {

				@Override
				public void run() {
					System.out.println("index:" + index);
					try {
						Thread.sleep(200);
					} catch (Exception e) {
						// TODO: handle exception
					}
				}
			});
		}

	}

}

如圖所示:
在這裏插入圖片描述

e).線程池的原理分析

看源碼:這裏拿newFixedThreadPool舉栗子,點擊newFixedThreadPool的execute進入ThreadPoolExcutor

  /**
     * Executes the given task sometime in the future.  The task
     * may execute in a new thread or in an existing pooled thread.
     *
     * If the task cannot be submitted for execution, either because this
     * executor has been shutdown or because its capacity has been reached,
     * the task is handled by the current {@code RejectedExecutionHandler}.
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {         
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

核心:

最主要的區別就是核心線程數和最大線程數量:

a).用戶線程>核心線程數 -----> 將大於核心線程的數量放在隊列線程中 -----> 繼續執行任務 --------> 用戶線程>緩存隊列線程 ---------> 將大於最大線程數量 ------> 放在最大線程池裏邊 -----> 用戶線程>最大線程 -------> 出現異常

自己來一個畫圖:
在這裏插入圖片描述

多線程總結:
四種線程池都是基於ThreadPoolExecutor,但是是根據不同的構造方法來搞的,主要是四個參數,根據不同的構造方法來完成不同的需求。
參數說明:
CorePoolSize:核心線程池的大小。當任務來了之後就會執行任務,如果當前線程達到了CorePoolSize十週,就會給線程放在緩存池中。。
MaxnumPoolSize:線程池的最大數量。表示的是線程池中最多創建的線程池數量。
keepAliveTime:表示的當前線程池沒有變成線程執行時,保持多久時間會終止。
unit:表示的是keepAliveTime的時間單位。主要有年月日,時分秒

f).合理的配置線程數數量

主要從兩個方面來考慮線程數量
a)CPU密集 b).I/O密集
https://blog.csdn.net/wt520it/article/details/87894309

CPU密集型時,任務可以少配置線程數,大概和機器的cpu核數相當,這樣可以使得每個線程都在執行任務
IO密集型時,大部分線程都阻塞,故需要多配置線程數,2*cpu核數
操作系統之名稱解釋:
某些進程花費了絕大多數時間在計算上,而其他則在等待I/O上花費了大多是時間,
前者稱爲計算密集型(CPU密集型)computer-bound,後者稱爲I/O密集型,I/O-bound。

在實戰中學習,在快樂中成長

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