线程池

·为什么要使用线程池?
使用线程池是为了节省资源,如果每次有任务要来执行,每次都重新创建一个线程,使用完之后再销毁,创建和销毁线程池的代价是很大的,这很划算不来;所以才引进了线程池;
且使用了线程池后,如果当有任务进来时,如果此时线程池内有空闲线程可以来处理该任务,那么就可以直接执行该任务,不用再等待创建线程池了,还提高了效率;

·线程池的使用
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
通过该构造函数来创建线程池;

(1)corePoolSize:核心池的大小;

(2)maximumPoolSize:线程池最大线程数量;
corePoolSize和maximumPoolSize的数量大小,可以相等也可以不相等;

(3)keepAliveTime:线程保持活动时间,即线程池的工作线程空闲后,保持存活的时间;
(4)TimeUnit :keepAliveTime的时间单位

(5)workQueue(工作队列):用于保存等待执行任务的阻塞队列。
可以选择以下几个阻塞队列:
A.ArrayBlockingQueue:基于数组结构的有界阻塞队列,此队按照FIFO原则对元素进行排序;

B.LinkBlockingQueue:基于链表结构的阻塞队列,链表是没有数量限制的,按照FIFO排序元素,吞吐量高于ArrayBlockingQueue;

C.SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞队列,通常吞吐量比LinkedBlockingQueue还要高;

D.PrioityBlockingQueue:具有优先级的无界阻塞队列

(6)RejectedExecutionHandler (饱和策略):当队列和线程都满了,线程池会采用什么手段来处理新任务,默认采用AbortPolicy
JDK一共内置4个饱和策略:
A.AbortPolicy:表示无法处理新任务抛出异常,JDK默认采用此策略
B.CallerRunPolicy:等待调用者线程空闲后运行任务;
C.DiscardOldestPolicy:丢弃阻塞队列中最近的一个任务并执行当前任务;
D.DiscardPolicy:不处理,直接将新任务丢弃,也不报错;

且一般在创建线程池时,如果不想换饱和策略,RejectedExecutionHandler这一项可以省略不写,会自动使用默认的饱和策略;

·线程池的执行流程:
当一个Runnable或Callable对象到达线程池时:
第一步:首先判断核心线程池中线程是否有空闲线程, 如果有空闲线程,则将任务直接分配给空闲线程执行;如果没有空闲线程,则查看核心线程池中线程数量是否已经达corePoolSize的大小了,如果线程数量没达到corePoolSize,则创建线程执行任务;如果线程数量达到corePoolSize了,则执行第二步;

第二步:判断工作队列(BlockingQueue)是否已满,如果工作队列没有满,将提交任务存储到工作队列中等待核心池的调度;如果工作队列也满了,则执行第三步;

第三步:判断当前线程池中的线程数是否已达到了最大值maxiumSize,
若已达到最大值maxiumSize,将任务交给饱和策略处理;
否则,继续创建新线程执行此任务。

·线程池有两种提交任务的方式:
execute和submit;
execute由于提交没有返回值的任务;
submit用于提交有返回值的任务;

用execute来提交任务: Runnable是没有返回值的,可以用execute来提交

import java.util.concurrent.*;
class MyThread implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"-"+i);
        }
    }
}
public class Test{
    public static void main(String[] args) {
        ExecutorService executorService=new ThreadPoolExecutor(3,5,
                2000,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
       MyThread myThread=new MyThread();
        for(int i=0;i<5;i++){
            executorService.execute(myThread);
        }
        executorService.shutdown();
    }
}

因为采用的工作队列是 LinkedBlockingQueue,链表中存放数据没有大小限制,所以当核心线程池满时,进来的任务就会进入到阻塞对列中,而阻塞队列中存放任务数量没有限制,所以创建线程池不会超过核心线程池的大小;通过运行结果也可以看出:
在这里插入图片描述

**用submit来提交任务:**结果可以用Future来接收,使用get方法来获取返回值。Callable是有返回值的,可以用submit来提交

import java.util.concurrent.*;
class YourThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        for(int i=0;i<6;i++){
            System.out.println(Thread.currentThread().getName()+"-"+i);
        }
        return Thread.currentThread().getName()+"执行完毕";
    }
}
public class Test {
   public static void main(String[] args) {
        ExecutorService executorService=new ThreadPoolExecutor(3,5,
                2000,TimeUnit.MILLISECONDS,new  LinkedBlockingQueue<>());
        YourThread myThread=new YourThread();
        for(int i=0;i<5;i++){
            executorService.submit(myThread);
            //或者可以使用Future 来接收,使用get方法来获取返回值
          /*  Future future=executorService.submit(myThread);//用Future.get来接收返回值与 不用Future.get来接收返回值,结果运行有很大差别
            try {
                System.out.println(future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }*/
        }
        executorService.shutdown();
    }
}

未使用Futute.get()来接受返回值:
在这里插入图片描述

使用Futute.get()来接受返回值:

在这里插入图片描述
结果用不用Future.get()来接收返回值,运行的结果有很大差异;原因是:Future.get()会阻塞其他线程,一直等到当前Callable线程执行完毕拿到返回值为止(是调用了Future的get()方法;如果只是使用了Future来接收,但并没有使用它的get()方法来接收返回值,则不会出现阻塞的现象)。

使用完线程池要记得关闭:shutdown;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值