線程和線程池

線程

線程的幾種狀態

public enum State {
        //創建後尚未啓動的線程處於這種狀態。
        NEW,

        //Runable包括了操作系統現線程狀態中的Runing和Ready,也就是處於次狀態的線程有可能正在執行,也有可能正在等待着CPU爲它分配執行時間。
        RUNNABLE,

        //線程被阻塞了,“阻塞狀態”與“等待狀態”的區別是:“阻塞狀態”在等待的時候有一個排它鎖,這個事件將在另外一個線程放棄這個鎖的時候發生;“等待狀態”則是等待一段世間,或者喚醒動作的發生。在程序等待進入同步區域的時候,線程進入這種狀態。
        BLOCKED,

        //處於這種狀態的線程不會被分配CPU執行時間,它們要等待被其他線程顯式的喚醒。
        WAITING,

        //處於這種狀態的縣城也不會被分配CPU執行時間,不過無需等待被其它線程顯式的喚醒,在一定時間之後它們會由系統自動喚醒,在一定時間之後它們會由系統自動喚醒。
        TIMED_WAITING,

        //已終止線程的線程狀態,線程已經結束執行。
        TERMINATED;
    }

實現線程的幾種方式(4種)

1)繼承Thread類,實現run方法

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    }
    
}
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("------------");

 

2)實現Runnable接口,實現run方法

public class MyRunable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}
        Thread thread = new Thread(new MyRunable());
        thread.start();
        System.out.println("------------");

 3)實現Callable接口,實現call方法

和上面的方式相比,這種有返回結果

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {

        int sum = 0;

        for (int i = 0; i < 10; i++) {
            sum += i;
            System.out.println(Thread.currentThread().getName() + "----" + i);
        }
        return sum;
    }
}
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println("---------------");
        Integer result = futureTask.get();//get方法是阻塞方法,只有當自定義的線程運行完纔會得到結果
        System.out.println("result: " + result);

 4)線程池創建線程

//創建線程池
        ExecutorService pool = Executors.newFixedThreadPool(5);
        //爲線程池中的線程分配任務
        MyCallable myCallable = new MyCallable();
        Future<Integer> result = pool.submit(myCallable);
        //關閉線程池
        pool.shutdown();
        System.out.println(result.get());

 

線程池

線程池工作原理

1 先向核心線程 提交任務

2 如果核心線程滿了 把任務放在隊列中

3 如果隊列也滿了 ,那就擴招 非核心線程

4 最大線程 和 任務隊列都滿了,就執行拒絕策略

線程池的核心參數

以下面爲例

ExecutorService executorService = Executors.newFixedThreadPool(5);//一個池子有5個線程

跟一下newFixedThreadPool方法

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

繼續跟

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

繼續跟

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

此時ThreadPoolExecuter的7個參數就出來了

1)int corePoolSize

線程池中常駐的核心線程數

2)int maximumPoolSize 

線程池中允許同時容納執行的最大線程數,此值必須大於等於1

3)long keepAliveTime

多餘的空閒線程的存活時間,當前池中線程數量超過corePoolSize時,當空閒時間達到keepAliveTime時,多餘線程會被銷燬直到只剩下corePoolSize

4)TimeUnit unit

keepAliveTime的單位

5)BlockingQueue<Runnable> workQueue

任務隊列,被提交但尚未被執行的任務

6)ThreadFactory threadFactory

表示生成線程池中工作線程的線程工程,用於創建線程,一般默認即可

7)RejectedExecutionHandler handler

拒絕策略,表示當隊列滿了,並且工作線程大於等於線程池的最大線程數(maximumPoolSize )時如何來拒絕請求執行的runable的策略

 

自定義線程池的原因

 

 

自定義線程池

代碼

ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

其中

corePoolSize=2

maximumPoolSize=5

keepAliveTime=2

TimeUnit=秒

workQueue=new LinkedBlockingQueue<Runnable>(3) ,裏面的構造方法傳入3,否則默認爲Integer.max(源碼)

threadFactory=Executors.defaultThreadFactory()  一般就用Executors的默認的線程工廠

handler=new ThreadPoolExecutor.AbortPolicy() 這個是線程池默認的拒絕策略

 

爲什麼這麼配?

corePoolSize=2    maximumPoolSize=5   這兩個數怎來的?

如果是CPU密集型,maximumPoolSize=CPU核數+1

CPU核數怎麼看:

System.out.println(Runtime.getRuntime().availableProcessors());

如果是IO密集型,maximumPoolSize=2*CPU (這個不太準備,自行百度)

參考:

什麼是CPU密集型、IO密集型?

https://blog.csdn.net/youanyyou/article/details/78990156

拒絕策略

  • 1) new ThreadPoolExecutor.AbortPolicy()(默認)

---->這種拒絕策略當達到maximumPoolSize+隊列最大值後就會中斷報異常

ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

此定義的線程爲最大線程數爲5,阻塞隊列爲3,也就是最大運行同時提交8個線程,如果我同時提交8個線程,那沒有問題,運行5個,3個在隊列中,沒有問題

如果我現在同時運行9個,那就會觸發拒絕策略,測試代碼如下

public class Start {

    public static void main(String[] args) {

        //自定義線程池,最大線程數爲5,等待隊列最大爲3,最大運行同時提交最大線程數爲5+3=8
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());


        try {
            for (int i = 0; i < 9; i++) {
                int finalI = i;
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(3);//睡3秒,才能達到併發目的
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + finalI);

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

 

 

  • 2)new ThreadPoolExecutor.CallerRunsPolicy()

----> 該策略既不會拋拋棄任務,也不會拋出異常,而是將某些任務退到調用者,從而降低新任務的流量。

例如下面的測試代碼,線程池最大運行8個(5個運行,3個等待),第九個就回退給main方法區運行

    public static void main(String[] args) {

        //自定義線程池,最大線程數爲5,等待隊列最大爲3,最大運行同時提交最大線程數爲5+3=8
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());


        try {
            for (int i = 0; i < 9; i++) {
                int finalI = i;
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(3);//睡3秒,才能達到併發目的
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + finalI);

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

  • 3)new ThreadPoolExecutor.DiscardPolicy()

---->該策略默默的丟棄無法處理的任務,不予任何處理也不拋棄異常。如果運行任務丟失,這是最好的一種策略。

例如下面測試代碼,丟棄了第9個任務

public class Start {

    public static void main(String[] args) {

        //自定義線程池,最大線程數爲5,等待隊列最大爲3,最大運行同時提交最大線程數爲5+3=8
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());


        try {
            for (int i = 0; i < 9; i++) {
                int finalI = i;
                executorService.execute(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(3);//睡3秒,才能達到併發目的
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                    }
                    System.out.println(Thread.currentThread().getName() + "\t" + finalI);

                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

 

  • 4)new ThreadPoolExecutor.DiscardOldestPolicy()

---->拋棄隊列中等待最久的任務,然後把當前任務加入到隊列中嘗試再次提交當前任務(換句話說:長江後浪推前浪,把前浪拍死在沙灘上)

類似上面new ThreadPoolExecutor.DiscardPolicy()

 

 

發佈了96 篇原創文章 · 獲贊 115 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章