Java线程池

    public static void main(String[] args) throws InterruptedException {
        Long start = System.currentTimeMillis();
        final Random random = new Random();
        final List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < 100000; i++) {
            Thread thread = new Thread() {
                @Override
                public void run() {
                    list.add(random.nextInt());

                }
            };
            thread.start();
            thread.join();
        }
        System.out.println("时间:" + (System.currentTimeMillis() - start));
        System.out.println("大小:" + list.size());

    }

使用线程的方式去执行程序

使用线程池

    public static void main(String[] args) throws InterruptedException {
        Long start = System.currentTimeMillis();
        final Random random = new Random();
        final List<Integer> list = new ArrayList<Integer>();
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 100000; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    list.add(random.nextInt());
                }
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);
        System.out.println("时间:"+(System.currentTimeMillis() - start));
        System.out.println("大小:"+list.size());


    }

由上述对比结果可知,执行相同的逻辑,在时间上使用单个线程耗时更长。

原因

1.使用Thread的程序,每循环一次就创建一个线程,而CPU中是会发生线程的上下文切换的,这个切换会消耗CPU的性能,所以从时间上造成的结果就是使用线程进行程序耗时更长

2.而使用线程池的程序,会对池中的线程进行复用,没有频繁的上下文切换

线程池参数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
  1. 核心线程数:指的是线程池中核运行的线程数量大小
  2. 最大线程数:指的是池中允许创建的最大的线程的数量
  3. 空闲线程存活时间:指的是当线程池中一个线程处于空闲状态,且存活的线程数量大于核心线程数,在超过一定时间之后,这个空闲线程就会被销毁
  4. 空闲线程存活时间单位:指的是具体的存活时间使用的单位
  5. 阻塞队列:指的是当新任务被提交,会先进入到队列中,在调度的时候再从队列中取出,JDK中常用的队列有
  • SynchronousQueue:不缓存任务的工作队列,即是当有任务进来,若有空闲线程,则直接调度;若没有,则创建新线程进行调度,newCachedThreadPool就是使用这种队列
  • LinkedBlockingQuene:基于链表的无界限阻塞队列,容量为 Integer.MAX_VALUE,当有新任务进来,且线程数量达到核心线程数,就会往这个队列中存放任务,但不会创建新线程,JDK中的newFixedThreadPool和newSingleThreadExecutor就是使用该队列

线程池种类

  • newCachedThreadPool
  • newFixedThreadPool
  • newSingleThreadExecutor
  • ThreadPoolExecutor(自定义线程池)

比较各个线程池

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();//快
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();//最慢

 
        for (int i = 1; i <= 100; i++) {
            executorService1.execute(new MyTask(i));

        }
    }
}

class MyTask implements Runnable {
    int i = 0;

    public MyTask(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "程序员做第" + i + "个项目");
        try {
            Thread.sleep(3000L);//业务逻辑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

分别使用者三种线程池运行程序

可以得出,executorService1最快,executorService2次之,executorService3最慢

从源码层次分析原因

newFixedThreadPool实现如下

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

newSingleThreadExecutor实现如下

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

对比主要的参数区别

线程池种类/参数设置

corePoolSize

maximumPoolSize

workQueue

newCachedThreadPool

0

Integer.MAX_VALUE

SynchronousQueue

newFixedThreadPool

nThreads

nThreads

LinkedBlockingQueue

newSingleThreadExecutor

1

1

LinkedBlockingQueue

由对比结果可知,newCachedThreadPool可创建的线程数量没有上限,且任务来了就可以直接执行,所以它执行的速度最快;而newFixedThreadPool可创建的线程数量随初始化线程池时定下的nThreads决定,且任务进来以后是放在队列中进行等待的,只有池中有空闲的线程才会从队列中取出(FIFO)进行调度;相比第一个线程池,它得等待之前的线程空闲才能执行,所以时间稍慢;而newSingleThreadExecutor,它只有一个线程进行工作,后续的任务只能依靠这一个线程来执行,所以最慢。

自定义线程池

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(10));
        for (int i = 1; i <= 120; i++) {
            threadPoolExecutor.execute(new MyTask(i));

        }
    }
}

class MyTask implements Runnable {
    int i = 0;

    public MyTask(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "程序员做第" + i + "个项目");
        try {
            Thread.sleep(3000L);//业务逻辑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果

触发了拒绝策略

分析线程池执行的代码

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            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);
    }

 

执行流程

  1. 判断工作线程数量是否小于核心线程数,若是,使用新提交的任务创建线程,若创建成功则提交任务执行
  2. 若大于等于核心线程数,判断线程池是否还属于运行状态、且入参的任务能成功添加到队列,然后进行再次检测,若线程池不是运行状态且入参任务被移除,使用拒绝策略拒绝任务;若不是,再判断当前线程数量是否为0,若是,创建一个线程空跑。
  3. 若新增线程失败,执行拒绝策略

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值