线程池全面解析

0、目录

一、介绍

二、工作原理

三、使用

四、Java中自动的四种线程池

五、Future和FutureTask

六、总结

一、介绍

二、工作原理

首先介绍线程池的类图:

 

ThreadPoolExecutor类是线程池的真正实现类,可以根据不同的需求配置相关的参数,从而实现自定义线程池。(一般不需要自定义线程池,Java内部已经实现了四个常用的线程池,且都已经配置好了相关的参数)

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

各个参数描述如下:

线程池的工作逻辑:

三、使用

1、创建线程池
    //创建时,需要自己配置相关的参数(除非需要,一般不这么创建,Java中已有四种常见的线程创建方法)
 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
2、向线程池提交要执行的任务
    a、execute()方法传入Runnable对象
        executor.execute(new Runnable(){
            @ovveride
            public void run(){
                //新线程中要执行的任务
            }
        });
    或b、submit()方法传入Runnable对象
        executor.submit(new Runnable(){
            @override
            public void(){
                //新线程中要执行的任务
            }
        });
    或c、submit()方法传入Callable对象
        executor.submit(new Callable<Object>() {
              @Override
              public Object call() {
                  return null;
              }
          });

execute()和submit()区别

1、定义和具体实现位置不同。execute(Runnable command)归属于Executor接口;submit(Callable<T> task), submit(Runnable task, T result), submit(Runnable task)归属于ExecutorService接口。ExecutorService继承于Executor。

execute()方法在ThreadPoolExecutor类中具体实现,submit()方法在AbstractExecutorService类中具体实现。

2、submit()有返回值,而execute()没有返回。返回值类型为Future,可以根据返回结果查看任务执行情况。submit()方便做异常处理,通过Future.get()可以捕获异常。

Runnable于Callable的区别

定义如下:
public interface Runnable {
    void run();
}
public interface Callable<V> {
    V call() throws Exception;
}

通过定义可以看到:

1、Runnable执行的方法是run(),Callable执行的方法是call()

2、实现Runnable接口的任务线程没有返回值,而实现Callable接口的任务能返回执行结果

3、call()方法能够抛出异常,而run()方法则不能抛出异常,如果出现异常智能内部处理。

3、关闭线程池

executor.shutdown();

     或  executor.shutdownNow();

shutdown()于shutdownNow()的区别:

shutdown:不再接收新的任务,同时将线程池的状态设置为SHUTDOWN状态,已经添加的任务会继续执行直到完成(包括已经加入到任务队列中的);

shutdownNow:不再接收新的任务,将线程池的状态设置未STOP状态,会尝试停止正在执行的任务,并返回那些没有被执行的任务。

这里也仅仅是试图终止正在执行的线程,源码中是通过Thread.interrupt()方法来实现的,而interrupt()方法有一定的局限性,它仅仅是为线程设定了一个状态而已,并不代表就能立即停止正在执行的任务,所以要小心。

四、Java中自动的四种线程池

这四种线程池是:
    Executors.newCachedThreadPool():可缓存的线程池
    Executors.newFixedThreadPool():定长的线程池
    Executors.newScheduledThreadPool():定时的线程池
    Executors.newSingleThreadExecutor():单线程线程池
Executors是一个工具类,类似于Collections,提供工厂方法来创建不同类型的线程池。

4.1、可缓存的线程池(Executors.newCachedThreadPool())

定义如下:

可以看到核心线程数量为0,线程最大数量为Integer.MAX_VALUE,也就是说这类线程池没有核心线程,只有非核心线程,且数量不固定,可以无限大,任务到来后可立即执行,不需等待,空闲线程默认保活60s,之后会被回收。适合执行大量且耗时少的线程任务。不过如果线程无限增长,会导致内存溢出。

4.2、定长线程池(Executors.newFixedThreadPool())

定义如下:

可以看到它的corePoolSize = maximumPoolSize,也就是说它只有核心线程而没有非核心线程,每个线程池中最多只能有nThreads个线程,即可控制线程最大并发数量,超出的线程会在队列中等待。缺点就是大小固定,难以扩展。

4.3、定时的线程池(Executors.newScheduledThreadPool())

定义如下:

super()指的是:

可以看大到核心线程数量是固定的,非核心线程的数量是Integer.MAX_VALUE,可以说是无限大,主要应用于执行定时或周期性的任务

使用:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
//延时100毫秒后执行任务
executor.schedule(new Runnable() {
            @Override
            public void run() {
                //任务
            }
        }, 100, TimeUnit.MICROSECONDS);
//延时0毫秒,每100毫秒执行一次
executor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                //任务
            }
        }, 0, 100, TimeUnit.MICROSECONDS);

4.4、单线程化的线程池(Executors.newSingleThreadExecutor())

定义如下:

可以看到,corePoolSize = maxinumPoolSize = 1, 也就是只有一个核心线程,总线程数量为1,没有非核心线程,因为只有一个核心线程,所以可以保证所有任务按照指定的顺序在一个线程中执行,不需要处理线程同步的问题。它不适合做并发。

四种线程池的总结对比:

五、Future和FutureTask

1、Future

Future表示对执行任务的一些操作,比如取消任务,查询是否取消或完成,获取执行返回结果等,这个结果就是call()方法返回的值。常见方法有cancel(Boolean),isCancelled(), isDone(), get()等。

使用如:

                ExecutorService executor = Executors.newCachedThreadPool();
                Future<String> future = executor.submit(new Callable<String>() {
                    @Override
                    public String call() {
                        return "haha...";
                    }
                });

                try {
                    String str = future.get();
                    Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    e.getMessage();
                }

Toast显示为“haha...”

2、FutureTask

定义:

通过源码可以看到FutureTask实现了RunnableFuture<V>接口,而RunnableFuture又实现了Runnable接口和Future<V>接口(即可以通过Runnable接口实现线程,也可以通过Future取得线程执行完后的结果)。

而且FutureTask还可以包装Runnable和Callable<V>,如下:

可以看到,传入的Runnable会通过Executors.callable()方法转换为Callable型,即FutureTask最终都要执行Callable类型的任务。

由于FutureTask实现了Runnable,因此它就可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行,并且FutureTask实现了Future,所以可以直接通过get()方法获取执行结果。因此FutureTask既是Future、Runnable,又包装了Callable(如果是Runnable则会被转换成Callable)。

FutureTask的使用:

        FutureTask taskCallable =  new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "FutureTask(Callable)";
            }
        });
    //或:
        String result = "taskRunnable";
        FutureTask taskRunnable =   new FutureTask<String>(new Runnable() {
            @Override
            public void run() {
                //任务
            }
        }, result);

    //启动任务:(因为其继承Runnable,所以可以采用以下方式)
  	new Thread(taskRunnable).start(); 
        new Thread(taskCallable).start();
    //获取任务执行的结果:(因为继承了Future,所以可以获得执行结果,设置取消,查询是否取消或执行完成)
       taskCallable.get();
       taskRunnable.get();

六、总结

终于写完啦,哈哈哈哈哈哈哈哈哈

线程池在每一个应用中都会用到,但是要真的全面的去整理一遍,真的不容易,自己用了好几天,也建议您自己整理一遍,通过整理能够加深理解,加深记忆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值