8.線程池

在這裏插入圖片描述
例子:

//主類
public class Main {

    public static void main(String[] args) throws  Exception{
        BlockingQueue<Data>  queue=new LinkedBlockingQueue<Data>(10);


        //生產者
        Provider p1 = new Provider(queue);
        Provider p2 = new Provider(queue);
        Provider p3 = new Provider(queue);

        //消費者
        Consumer c1 = new Consumer(queue);
        Consumer c2 = new Consumer(queue);
        Consumer c3 = new Consumer(queue);



       //創建線程池
        ExecutorService cachePool = Executors.newCachedThreadPool();
        cachePool.execute(p1);
        cachePool.execute(p2);
        cachePool.execute(p3);
        cachePool.execute(c1);
        cachePool.execute(c2);
        cachePool.execute(c3);



        try {

            Thread.sleep(3000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        p1.stop();
        p2.stop();
        p3.stop();

        try {

            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

    }



}
//生產者
public class Provider implements Runnable {

    //共享緩存區
    private BlockingQueue<Data> queue;

    private volatile boolean isRunning =true;

    private static AtomicInteger count= new AtomicInteger();

    private static Random r = new Random();

    public Provider(BlockingQueue queue){
        this.queue=queue;
    }



    @Override
    public void run() {
        while (isRunning){
            try {
                Thread.sleep(r.nextInt(1000));
                //獲取的數據進行累計
                int id = count.incrementAndGet();
                //比如通過一個getData方法獲取了
                Data data = new Data(Integer.toString(id), "數據" + id);
                System.out.println("當前線程:"+Thread.currentThread().getName()+",獲取了數據Id," +data.getId()+
                        "裝載到queue中去");
                //2秒鐘沒有加進去,返回false
                if (!this.queue.offer(data,2, TimeUnit.SECONDS)){
                    System.out.println("提交到Queue數據失敗");

                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

    }

    public  void stop(){
        this.isRunning=false;
    }
}

//消費者
public class Consumer  implements  Runnable{

    private BlockingQueue<Data> queue;

    public Consumer(BlockingQueue queue){
        this.queue=queue;
    }


    //隨機對象
    private static Random r =new Random();

    @Override
    public void run() {

        while (true){
            try {
                Data data = this.queue.take();
                //模仿數據處理
                Thread.sleep(r.nextInt(1000));
                System.out.println("當前消費線程:"+Thread.currentThread().getName()+",消費成功" +
                        "消費數據Id爲:"+data.getId());
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
//data類
public class Data {
    private String id;
    private String name;
    public Data(String id,String name){
        this.id=id;
        this.name=name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Data{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
輸出結果:

當前線程:pool-1-thread-1,獲取了數據Id,1裝載到queue中去
當前線程:pool-1-thread-3,獲取了數據Id,2裝載到queue中去
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:2
當前線程:pool-1-thread-1,獲取了數據Id,3裝載到queue中去
當前線程:pool-1-thread-3,獲取了數據Id,4裝載到queue中去
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:4
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:3
當前線程:pool-1-thread-2,獲取了數據Id,5裝載到queue中去
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:5
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:1
當前線程:pool-1-thread-2,獲取了數據Id,6裝載到queue中去
當前線程:pool-1-thread-1,獲取了數據Id,7裝載到queue中去
當前線程:pool-1-thread-3,獲取了數據Id,8裝載到queue中去
當前線程:pool-1-thread-1,獲取了數據Id,9裝載到queue中去
當前線程:pool-1-thread-2,獲取了數據Id,10裝載到queue中去
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:6
當前線程:pool-1-thread-3,獲取了數據Id,11裝載到queue中去
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:9
當前線程:pool-1-thread-2,獲取了數據Id,12裝載到queue中去
當前線程:pool-1-thread-1,獲取了數據Id,13裝載到queue中去
當前線程:pool-1-thread-2,獲取了數據Id,14裝載到queue中去
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:8
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:7
當前線程:pool-1-thread-2,獲取了數據Id,15裝載到queue中去
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:11
當前線程:pool-1-thread-2,獲取了數據Id,16裝載到queue中去
當前線程:pool-1-thread-3,獲取了數據Id,17裝載到queue中去
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:13
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:10
當前線程:pool-1-thread-2,獲取了數據Id,18裝載到queue中去
當前線程:pool-1-thread-3,獲取了數據Id,19裝載到queue中去
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:14
當前線程:pool-1-thread-3,獲取了數據Id,20裝載到queue中去
當前線程:pool-1-thread-1,獲取了數據Id,21裝載到queue中去
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:12
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:16
當前線程:pool-1-thread-2,獲取了數據Id,22裝載到queue中去
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:15
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:17
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:18
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:20
當前消費線程:pool-1-thread-6,消費成功消費數據Id爲:19
當前消費線程:pool-1-thread-4,消費成功消費數據Id爲:21
當前消費線程:pool-1-thread-5,消費成功消費數據Id爲:22



Executor框架:

Executor框架

發現都是調用底層的同一個方法ThreadPoolExecutor()

實現自定義線程池:
在這裏插入圖片描述

第一個參數corePoolSize:核心線程
表示線程池初始化的線程數量
第二個參數maxinumPoolSize:線程池最大線程數量
第三四個參數:表示線程執行完之後,多長時間回收 0表示線程執行完立刻回收
第五個參數:任務多了會放在該隊列中
第七個參數:拒絕策略

newScheduledThreadPool:

 class Temp extends Thread {
    public void run(){
        System.out.println("run");
    }

}


public class ScheduleJob{
    public static void main(String[] args) {
        Temp temp = new Temp();
        //初始化線程的數量
        ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
        //第二個參數1表示初始化1秒之後去執行的時間,第二個參數表示3秒輪詢
        schedule.scheduleWithFixedDelay(temp,1,3, TimeUnit.SECONDS);

    }
}

自定義線程池使用詳解:
自定義線程池使用詳解
例子:

1.有界隊列
public class UseThreadPoolExecutor1 {

public static void main(String[] args) {

    /**
     * 在使用有界隊列時,若有新的任務需要執行,如果線程池實際線程數小於corePoolSize,則優先創建線程,
     * 若大於corePoolSize,則會將任務加入隊列,
     * 若隊列已滿,則在總線程數不大於maximumPoolSize的前提下,創建新的線程,
     * 若線程數大於maximumPoolSize,則執行拒絕策略。或其他自定義方式。
     *
     */
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
            1,//核心線程數量
            2,// 最大線程數量
            60,//空閒時間
            TimeUnit.SECONDS,//空閒時間的單位
            new ArrayBlockingQueue<Runnable>(3)//指定一種隊列 (有界隊列)
    );

    MyTask mt1 = new MyTask(1, "任務1");
    MyTask mt2 = new MyTask(2, "任務2");
    MyTask mt3 = new MyTask(3, "任務3");
    MyTask mt4 = new MyTask(4, "任務4");
    MyTask mt5 = new MyTask(5, "任務5");
    MyTask mt6 = new MyTask(6, "任務6");

    pool.execute(mt1);
    pool.execute(mt2);
   /* pool.execute(mt3);
    pool.execute(mt4);
    pool.execute(mt5);*/
   // pool.execute(mt6);

    pool.shutdown();


}
}
public class MyTask implements Runnable {

	private int taskId;
	private String taskName;
	
	public MyTask(int taskId, String taskName){
		this.taskId = taskId;
		this.taskName = taskName;
	}
	
	public int getTaskId() {
		return taskId;
	}

	public void setTaskId(int taskId) {
		this.taskId = taskId;
	}

	public String getTaskName() {
		return taskName;
	}

	public void setTaskName(String taskName) {
		this.taskName = taskName;
	}

	@Override
	public void run() {
		try {
			System.out.println("run taskId =" + this.taskId);
			Thread.sleep(5*1000);
			//System.out.println("end taskId =" + this.taskId);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
	
	public String toString(){
		return Integer.toString(this.taskId);
	}

}

說明:
pool.execute(mt1);
pool.execute(mt2);
pool.shutdown();
運行結果:
run taskId =1
run taskId =2
當放2個任務的時候,因爲核心線程爲1,第二個任務會放在隊列裏面。第一個結果出來之後5秒鐘纔會輸出 run taskId =2 。

pool.execute(mt1);
pool.execute(mt2);
pool.execute(mt3);
pool.execute(mt4);
pool.execute(mt5);
pool.shutdown();

運行結果:
run taskId =1
run taskId =5
run taskId =2
run taskId =3
run taskId =4

放了5個任務已經,初始化一個線程執行一個,隊列裏面放3個 一共4個,超出的一個會另起一個線程。
所以先同時輸出run taskId =1 run taskId =5,5秒後在同時輸出run taskId =2 run taskId =3最後輸出run taskId =4
pool.execute(mt1);
pool.execute(mt2);
pool.execute(mt3);
pool.execute(mt4);
pool.execute(mt5);
pool.execute(mt6);
pool.shutdown();
放了6個任務,隊列滿了,而且超出線程池最大線程數量。會執行拒絕策略。第6個任務會拋出異常。

2.無界隊列

package com.bjsxt.height.concurrent018;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class UseThreadPoolExecutor2 implements Runnable{

	private static AtomicInteger count = new AtomicInteger(0);
	
	@Override
	public void run() {
		try {
			int temp = count.incrementAndGet();
			System.out.println("任務" + temp);
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) throws Exception{
		//System.out.println(Runtime.getRuntime().availableProcessors());
		BlockingQueue<Runnable> queue = 
				new LinkedBlockingQueue<Runnable>();
				//new ArrayBlockingQueue<Runnable>(10);
		ExecutorService executor  = new ThreadPoolExecutor(
					5, 		//core
					10, 	//max
					120L, 	//2fenzhong
					TimeUnit.SECONDS,
					queue);
		
		for(int i = 0 ; i < 20; i++){
			executor.execute(new UseThreadPoolExecutor2());
		}
		Thread.sleep(1000);
		System.out.println("queue size:" + queue.size());		//10
		Thread.sleep(2000);
	}


}

輸出結果:
任務2
任務1
任務3
任務4
任務5
queue size:15
任務6
任務7
任務8
任務10
任務9
任務11
任務12
任務13
任務15
任務14
任務16
任務17
任務18
任務20
任務19

說明:會5個5個的執行

策略:

public class UseThreadPoolExecutor1 {


    public static void main(String[] args) {
        /**
         * 在使用有界隊列時,若有新的任務需要執行,如果線程池實際線程數小於corePoolSize,則優先創建線程,
         * 若大於corePoolSize,則會將任務加入隊列,
         * 若隊列已滿,則在總線程數不大於maximumPoolSize的前提下,創建新的線程,
         * 若線程數大於maximumPoolSize,則執行拒絕策略。或其他自定義方式。
         *
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1, 				//coreSize
                2, 				//MaxSize
                60, 			//60
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3)			//指定一種隊列 (有界隊列)
                //new LinkedBlockingQueue<Runnable>()
               // , new MyRejected()
               , new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        MyTask mt1 = new MyTask(1, "任務1");
        MyTask mt2 = new MyTask(2, "任務2");
        MyTask mt3 = new MyTask(3, "任務3");
        MyTask mt4 = new MyTask(4, "任務4");
        MyTask mt5 = new MyTask(5, "任務5");
        MyTask mt6 = new MyTask(6, "任務6");

        pool.execute(mt1);
        pool.execute(mt2);
        pool.execute(mt3);
        pool.execute(mt4);
        pool.execute(mt5);
        pool.execute(mt6);

        pool.shutdown();

    }
}

DiscardOldestPolicy丟棄最老的任務

輸出結果:
run taskId =1
run taskId =5
run taskId =3
run taskId =4
run taskId =6
丟棄了任務2.。。

可以自定義策略:

例子:

public class UseThreadPoolExecutor1 {


    public static void main(String[] args) {
        /**
         * 在使用有界隊列時,若有新的任務需要執行,如果線程池實際線程數小於corePoolSize,則優先創建線程,
         * 若大於corePoolSize,則會將任務加入隊列,
         * 若隊列已滿,則在總線程數不大於maximumPoolSize的前提下,創建新的線程,
         * 若線程數大於maximumPoolSize,則執行拒絕策略。或其他自定義方式。
         *
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1, 				//coreSize
                2, 				//MaxSize
                60, 			//60
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3)			//指定一種隊列 (有界隊列)
                //new LinkedBlockingQueue<Runnable>()
              , new MyRejected()
              // , new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        MyTask mt1 = new MyTask(1, "任務1");
        MyTask mt2 = new MyTask(2, "任務2");
        MyTask mt3 = new MyTask(3, "任務3");
        MyTask mt4 = new MyTask(4, "任務4");
        MyTask mt5 = new MyTask(5, "任務5");
        MyTask mt6 = new MyTask(6, "任務6");

        pool.execute(mt1);
        pool.execute(mt2);
        pool.execute(mt3);
        pool.execute(mt4);
        pool.execute(mt5);
        pool.execute(mt6);

        pool.shutdown();

    }
}

//拒絕的類
public class MyRejected implements RejectedExecutionHandler{

	
	public MyRejected(){
	}
	
	@Override
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		System.out.println("自定義處理..");
		System.out.println("當前被拒絕任務爲:" + r.toString());
		

	}

}
實現了RejectedExecutionHandler接口

運行結果:

自定義處理…
run taskId =1
run taskId =5
當前被拒絕任務爲:6
run taskId =2
run taskId =3
run taskId =4

可以在自定義策略裏http請求返回給任務提交者,或者保存到日誌,再次在後臺統一處理比如跑批處理

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