Java高并发--线程池

一种最简单得线程创建和回收得方式:

new Thread(new Runnable() {
            public void run() {
                //do sth
            }
        }).start();

上述方法在run()方法结束后,自动回收该线程。

线程池

为了避免得创建和销毁线程,我们可以对创建得线程进行复用。

线程池中,总有几个活跃得线程,需要使用线程时,从池中拿一个空闲线程,完成工作后,不急着关闭线程,而是将线程退回线程池,供复用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ytn9Glf3-1571453622882)(C:\Users\Nice\AppData\Roaming\Typora\typora-user-images\1571406651900.png)]

ThreadPoolExecutor表示一个线程池,Executors类扮演线程池工程得角色。

在这里插入图片描述

Executor框架提供了各种类型得线程池

//该方法返回一个固定线程数量得线程池。该该线程池中得数量始终不变。
public static ExecutorService newFixedThreadPool(int nThreads);

//该方法返回一个只有一个线程得线程池。若多余一个任务被提交到线程池,则按FIFO顺序执行任务
public static ExecutorService newSingleThreadExecutor();

//该方法返回一个可根据实际情况调整线程数量得线程池,若有空闲线程可以复用,则优先使用可复用线程。
public static ExecutorService newCachedThreadPool();

//线程池大小为1,ScheduledExecutorService接口扩展了一个给定时间执行某任务得功能。
public static ScheduledExecutorService newSingleThreadScheduledExecutor();

//该方法也返回一个ScheduledExecutorService对象。但可以指定线程数量
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

下面得实例创建了一个由5个线程得线程池,总共提交了十次任务

最终我们会发现,线程是5个先执行,隔了十秒钟,另外5个在执行。

public class ThreadPoolDemo {
    public static class MyTask implements Runnable{

        public void run() {
            System.out.println(System.currentTimeMillis()+": Thread ID: "+
                    Thread.currentThread().getId());
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        MyTask task = new MyTask();
        //创建了一个有5个线程得线程池
        ExecutorService es = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            es.submit(task);
        }
    }
}

计划任务

//在给定得时间delay后,会对任务进行一次调度
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

//剩下两个方法会对任务进行周期性得调度
//initDelay是执行第一个任务之前得延时,而period是前一个任务得开始到后一个任务开始得时间
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initDelay, 
                                             long period, TimeUnit unit);
//这里得delay是前一个任务得结束时间到后一个任务得开始时间
public ScheduledFuture<?> scheduleWithFixedRate(Runnable command, long initDelay,
                                               long delay, TimeUnit unit);
//对于后两个函数来说:period = delay + 任务执行得时间

在这里插入图片描述

核心线程池得内部实现

核心线程池:newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()方法。

//corePoolSize和maximumPoolSize大小一样,使用LinkedBlockingQueue任务队列存放无法立即执行得任务,入伍提交过于频繁容易导致资源耗尽。
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

//是上个方法得退化,只有一个线程
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

//没有任务时候,线程池中没有线程,任务被提交得时候,任务会加入SynchronousQueue队列,迫使线程池增加新的线程执行任务。
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

workQueue:是BlockingQueue接口得对象,仅用于存放Runnable对象:

  • 直接提交队列:SynchronousQueue对象提供,该对象没有容量。它提交得任务不会被真实保存,总是将新任务交给线程执行。如果没有空闲进程,则创建新的进行。如果线程到最大值,则执行拒绝策略。

  • 有界得任务队列:ArrayBlockingQueue实现,该队列有最大容量maximumPoolSize。若大于corePoolSize,则新任务加入等待队列。若大于maximumPoolSize,则执行拒绝策略。

  • 无界得任务队列:LinkedBlockingQueue实现,除非资源耗尽,不然不会出现任务入队失败得情况。

  • 优先任务队列:PriorityBlockingQueue实现,可以控制任务执行先后顺序。根据任务自身得优先级顺序先后执行

由以上线程池得实现代码可以看到,它们只是ThreadPoolExecutor类得封装。

public ThreadPoolExecutor(	 
    //指定线程池中得线程数量
    int corePoolSize,
    //指定线程池中最大线程得数量                          
    int maximumPoolSize,
    //当前线程数量数量超过corePoolSize时,多余空闲线程存活时间。即多久会被销毁                       
    long keepAliveTime,
    //keepAliveTime得单位
    TimeUnit unit,
    //任务队列,被提交尚未被执行得任务      
    BlockingQueue<Runnable> workQueue,
    //线程工厂,用于创建线程
    ThreadFactory threadFactory,
    //拒绝策略,任务太多时如何拒绝任务     
    RejectedExecutionHandler handler)
    
    //提交任务时候,此时如果线程小于corePoolSize则直接创建新线程执行任务
    //当corePoolSize<现在正在运行线程数<maximumPoolSize时候,则进入等待队列
    //当maximumPoolSize<现在得线程数,则执行拒绝策略。

下面是ThreadPoolExecutor得核心调度代码

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
    	int c = ctl.get();
    //workerCountOf()函数取得了当前线程池得线程总数
        if (workerCountOf(c) < corePoolSize) {
            //通过addWorker直接调度执行任务
            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);
    }

拒绝策略

ThreadPoolExecutor中最后一个参数指定了拒绝策略。

当任务数量超过系统实际承载能力时,调用拒绝策略,是系统超负荷运行时得补救措施。

//该策略会直接抛出异常,阻止系统正常运行
public static class AbortPolicy implements RejectedExecutionHandler;

//只要线程池未关闭,该策略就直接在调用者线程中使用,运行被丢弃得任务
public static class CallerRunsPolicy implements RejectedExecutionHandler;

//默默丢弃无法处理得任务,不予任何处理
public static class DiscardPolicy implements RejectedExecutionHandler;

//丢弃最老得一个请求,并尝试再次提交当前任务。
public static class DiscardOldestPolicy implements RejectedExecutionHandler;

如果上述策略无法满足实际应用需要,那么可以自己扩展RejectedExecutionHandler接口

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
public class RejectThreadPoolDemo {
    public static class MyTask implements Runnable{

        public void run() {
            System.out.println(System.currentTimeMillis()+": Thread ID:"+
                    Thread.currentThread().getId());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyTask task = new MyTask();
        //自定义线程池
        ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<Runnable>(10),
                Executors.defaultThreadFactory(),
                //自定义拒绝策略
                new RejectedExecutionHandler() {
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println(r.toString()+"is discard");
                    }
                });
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            es.submit(task);
            Thread.sleep(10);
        }
    }
}

自定义线程创建:ThreadFactory

线程池得主要作用是为了复用,避免线程频繁创建。而最开始得线程便是从:ThreadFactory而来。

public interface ThreadFactory { Thread newThread(Runnable r); }

实例:自定义线程

				new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        //创建新线程
                        Thread t = new Thread(r);
                        //设置为守护进程
                        t.setDaemon(true);
                        //添加打印信息
                        System.out.println("create"+t);
                        return t;
                    }
                },

扩展线程池

ThreadPoolExecutor是一个可以扩展得线程池,提供了beforeExecutor()、afterExecutor()、terminated()三个接口对线程池进行控制。

fork和join

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值