JAVA多线程(3):线程池

一、定义

1.作用

线程池:提供了一个级程队列,队列中保存着所有等特状态的线程。避免了创建与销象额外开销,提高了响应的速度。

2.体系结构

package java.util.concurrent.Executor:负责线程使用与调度的根接口
----》ExecutorService 子接口:线程池主要接口,内部方法基本满足线程池调度
 
--------》ThreadPoolExecutor:线程池的实现类
--------》ScheduledExecutorService 子接口:负责线程调度

3.工具类Executors

(1)创建固定大小的线程池

ExecutorService pool=Executors.newFixedThreadPool(10);

(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量

ExecutorService pool2=Executors.newCachedThreadPool();

(3)创建单个线程池,线程池只有一个线程

ExecutorService pool3=Executors.newSingleThreadExecutor();

(4)创建固定大小线程池,可以延迟或者定时地执行任务

ExecutorService pool4=Executors.newScheduledThreadPool(5);

二、使用线程池实例

1.步骤

(1)创建线程池
(2)向线程池提交任务
(3)关闭线程池

2.提交实现Runnable接口的线程

(1)提交单个任务

package thread.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class TestThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //1.创建线程池
        //(1)创建固定大小的线程池
//        Executor
//        ScheduledExecutorService
        ExecutorService pool=Executors.newFixedThreadPool(10);
        //(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
//        ExecutorService pool2=Executors.newCachedThreadPool();
        //(3)创建单个线程池,线程池只有一个线程
//        ExecutorService pool3=Executors.newSingleThreadExecutor();
        //(4)创建固定大小线程池,可以延迟或者定时地执行任务
//        ExecutorService pool4=Executors.newScheduledThreadPool(5);

        //2.为线程池分配任务
        //2.1 提交实现Runnable接口的实例
        ThreadPoolDemo threadPoolDemo=new ThreadPoolDemo();
        //(1)提交单个任务
        pool.submit(threadPoolDemo);

        //3.关闭线程池
        pool.shutdown();
    }
}

class ThreadPoolDemo implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<=10){
            System.out.println(Thread.currentThread().getName()+" : "+ ++i);
        }
    }
}

结果:

pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 : 5
pool-1-thread-1 : 6
pool-1-thread-1 : 7
pool-1-thread-1 : 8
pool-1-thread-1 : 9
pool-1-thread-1 : 10
pool-1-thread-1 : 11

(2)提交多个任务

package thread.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class TestThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //1.创建线程池
        //(1)创建固定大小的线程池
//        Executor
//        ScheduledExecutorService
        ExecutorService pool=Executors.newFixedThreadPool(5);
        //(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
//        ExecutorService pool2=Executors.newCachedThreadPool();
        //(3)创建单个线程池,线程池只有一个线程
//        ExecutorService pool3=Executors.newSingleThreadExecutor();
        //(4)创建固定大小线程池,可以延迟或者定时地执行任务
//        ExecutorService pool4=Executors.newScheduledThreadPool(5);

        //2.为线程池分配任务
        //2.1 提交实现Runnable接口的实例
        ThreadPoolDemo threadPoolDemo=new ThreadPoolDemo();
        //(1)提交单个任务
//        pool.submit(threadPoolDemo);
        //(2)提交20个任务
        for (int i = 0; i < 10; i++) {
            pool.submit(threadPoolDemo);
        }



        //3.关闭线程池
        pool.shutdown();
    }
}

class ThreadPoolDemo implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<=10){
            System.out.println(Thread.currentThread().getName()+" : "+ ++i);
        }
    }
}

结果:尽管提交了10个任务,但是线程池最多开了5个线程去执行

pool-1-thread-2 : 2
pool-1-thread-3 : 3
pool-1-thread-1 : 1
pool-1-thread-5 : 6
pool-1-thread-3 : 7
pool-1-thread-4 : 5
pool-1-thread-2 : 4
pool-1-thread-4 : 11
pool-1-thread-3 : 10
pool-1-thread-5 : 9
pool-1-thread-1 : 8

3.提交实现calable接口的线程

(1)提交单个任务

package thread.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class TestThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //1.创建线程池
        //(1)创建固定大小的线程池
//        Executor
//        ScheduledExecutorService
        ExecutorService pool=Executors.newFixedThreadPool(5);
        //(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
//        ExecutorService pool2=Executors.newCachedThreadPool();
        //(3)创建单个线程池,线程池只有一个线程
//        ExecutorService pool3=Executors.newSingleThreadExecutor();
        //(4)创建固定大小线程池,可以延迟或者定时地执行任务
//        ExecutorService pool4=Executors.newScheduledThreadPool(5);

        //2.为线程池分配任务
        //2.1 提交实现Runnable接口的实例
        ThreadPoolDemo threadPoolDemo=new ThreadPoolDemo();
        //(1)提交单个任务
//        pool.submit(threadPoolDemo);
        //(2)提交20个任务
//        for (int i = 0; i < 10; i++) {
//            pool.submit(threadPoolDemo);
//        }

        //2.2 提交实现Callable接口的实例
        //(1)提交单个Callable任务
        Future<Integer> future= pool.submit(new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                int sum=0;
                for (int i = 0; i <= 100; i++) {
                    sum+=i;
                }
                return sum;
            }
        });
        System.out.println(future.get());
 

        //3.关闭线程池
        pool.shutdown();
    }
}

class ThreadPoolDemo implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<=10){
            System.out.println(Thread.currentThread().getName()+" : "+ ++i);
        }
    }
}

结果:

5050
(2)提交多个任务

package thread.threadpool;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class TestThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //1.创建线程池
        //(1)创建固定大小的线程池
//        Executor
//        ScheduledExecutorService
        ExecutorService pool=Executors.newFixedThreadPool(5);
        //(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
//        ExecutorService pool2=Executors.newCachedThreadPool();
        //(3)创建单个线程池,线程池只有一个线程
//        ExecutorService pool3=Executors.newSingleThreadExecutor();
        //(4)创建固定大小线程池,可以延迟或者定时地执行任务
//        ExecutorService pool4=Executors.newScheduledThreadPool(5);

        //2.为线程池分配任务
        //2.1 提交实现Runnable接口的实例
        ThreadPoolDemo threadPoolDemo=new ThreadPoolDemo();
        //(1)提交单个任务
//        pool.submit(threadPoolDemo);
        //(2)提交20个任务
//        for (int i = 0; i < 10; i++) {
//            pool.submit(threadPoolDemo);
//        }

        //2.2 提交实现Callable接口的实例
        //(1)提交单个Callable任务
//        Future<Integer> future= pool.submit(new Callable<Integer>(){
//            @Override
//            public Integer call() throws Exception {
//                int sum=0;
//                for (int i = 0; i <= 100; i++) {
//                    sum+=i;
//                }
//                return sum;
//            }
//        });
//        System.out.println(future.get());
        //(2)提交20个任务
        List<Future<Integer>> list=new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Future<Integer> future= pool.submit(new Callable<Integer>(){
                @Override
                public Integer call() throws Exception {
                    int sum=0;
                    for (int i = 0; i <= 100; i++) {
                        sum+=i;
                    }
                    return sum;
                }
            });
            list.add(future);
        }
        for (Future<Integer> future : list) {
            System.out.println(future.get());
        }

        //3.关闭线程池
        pool.shutdown();
    }
}

class ThreadPoolDemo implements Runnable{
    private int i=0;
    @Override
    public void run() {
        while (i<=10){
            System.out.println(Thread.currentThread().getName()+" : "+ ++i);
        }
    }
}

结果:

5050
5050
5050
5050
5050
5050
5050
5050
5050
5050

三、线程池实现类解析

1.ExecutorService接口方法

(1)作用:
定义一个线程池的时候,往往都是使用这个接口:

        //1.创建线程池
        //(1)创建固定大小的线程池
//        Executor
//        ScheduledExecutorService
        ExecutorService pool=Executors.newFixedThreadPool(5);
        //(2)创建缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
//        ExecutorService pool2=Executors.newCachedThreadPool();
        //(3)创建单个线程池,线程池只有一个线程
//        ExecutorService pool3=Executors.newSingleThreadExecutor();
        //(4)创建固定大小线程池,可以延迟或者定时地执行任务
//        ExecutorService pool4=Executors.newScheduledThreadPool(5);

(2)接口中常用方法以及解释

public interface ExecutorService extends Executor {

	//关闭线程池,已提交的任务继续执行,不接受继续提交新任务
    void shutdown();


	//关闭线程池,尝试停止正在执行的所有任务,不接受继续提交新任务
	//它和前面的方法相比,加了一个单词“now”,所以它会去停止当前正在进行的任务
    List<Runnable> shutdownNow();

    //线程池是否已关闭
     boolean isShutdown();

    //如果调用了 shutdown()或shutdownNow()方法后,所有任务结束了,那么返回true
	//这个方法必须在调用shutdown或shutdownNow方法之后调用才会返回true 
    boolean isTerminated();

    //等待所有任务完成,并设置超时时间
	//我们这么理解,实际应用中是,先调用shutdown或shutdownNow
	//然后再调这个方法等待所有的线程真正地完成,返回值意味着有没有超时
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    //提交一个Callable任务
    <T> Future<T> submit(Callable<T> task);

    //提交一个Runnable任务,第二个参数将会放到Future中,作为返回值,
	//因为Runnable的run 方法本身并不返回任何东西
    <T> Future<T> submit(Runnable task, T result);

    //提交一个Runnable任务
    Future<?> submit(Runnable task);

    //执行所有任务,返回 Future类型的一个list
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;

    //也是执行所有任务,但是这里设置了超时时间
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)
        throws InterruptedException;

    //只有其中的一个任务结束了,就可以返回,返回执行完的那个任务的结果
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    //同上一个方法,只有其中的一个任务结束了,就可以返回,返回执行完的那个任务的结果,
	//不过这个带超时,超过指定的时间,抛出TimeoutException 异常
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

2.ThreadPoolExecutor

ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它实现了任务提交、线程管理、监控等等方法。
(1)构造方法

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

(2)参数解释

  • corePoolSize:核心线程数。
  • maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
  • workQueue:任务队列,BlockingQueue接口的某个实现(常使用ArrayBlockingQueue和LinkedBlockingQueue)。
  • keepAliveTime:空闲线程的保活时间,如果某线程的空闲时间超过这个值都没有任务给它做,那么可以被关闭了。注意这个值并不会对所有线程起作用,如果线程池中的线程数少于等于核心线程数corePoolSize,那么这些线程不会因为空闲太长时间而被关闭,当然,也可以通过调用allowCoreThreadTimeOut(true)使核心线程数内的线程也可以被回收。
  • threadFactory:用于生成线程,一般我们可以用默认的就可以了。通常,我们可以通过它将我们的线程的名字设置得比较可读一些,如Message-Thread-1,Message-Thread-2类似这样。
  • handler:当线程池已经满了,但是又有新的任务提交的时候,该采取什么策略由这个来指定。有几种方式可供选择,像抛出异常、直接拒绝然后返回等,也可以自己实现相应的接口实现自己的逻辑。

(3)流程

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDZVKSon-1593035570853)(G:\workspace\csdn\learn-document\java\concurrent\image-20200623162344913.png)]

四、线程池的4种拒绝策略 

参考

1.拒绝时机


线程池会在以下两种情况下会拒绝新提交的任务

当我们调用 shutdown 等方法关闭线程池的时候,如果此时继续向线程池提交任务,就会被拒绝
当任务队列(workQueue)已满,而且线程达到最大线程数(maximumPoolSize),如果再增加任务,也会被拒绝


2.拒绝策略

2.1 核心接口

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}


2.2 内建实现

2.2.1 CallerRunsPolicy

java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy

当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。

第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。
第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。


2.2.2 AbortPolicy

java.util.concurrent.ThreadPoolExecutor.AbortPolicy

拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。

2.2.3 DiscardPolicy

java.util.concurrent.ThreadPoolExecutor.DiscardPolicy

当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。

2.2.4 DiscardOldestPolicy

java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy

如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值