我承包了这片 "线程池"

2020-05-20  本文已影响0人  JackDaddy

上一篇我们讲到 Java 中关于线程的一些小知识,比如开启一个新的线程,线程的生命周期,线程中的变量等。那我们为什么要新开一个线程呢?
1)加快程序的执行速度
2)防止线程阻塞导致线程崩溃
3)在Android 4.0明确必须把耗时操作放在子线程执行,主线程只能执行UI操作

但我们要知道,线程在系统里其实是个稀缺又宝贵的资源,同时,频繁地开启 / 关闭线程也是很耗费系统资源的,为什么这么说的?

因此就提出了 "线程池" 的概念,将一些线程保存到一个容器里面,当需要使用线程时直接从该容器里取,使用完成后再放回容器里。

Java中的线程池

1)我们先来了解一下在 Java 中线程池的 "生命联系":
我们平时使用线程池时经常用到的类是 ThreadPoolExecutor 这个类,进入源码可以发现,他是继承自AbstractExecutorService ,然后进去到 AbstractExecutorService ,发现他实现了 ExecutorService 这个接口,而 ExecutorService 又继承自 Executor 这个接口,因此,实际上我们经常用的 ThreadPoolExecutor 是实现了 Executor这个接口 。

public class ThreadPoolExecutor extends AbstractExecutorService
------------------------------------------------------------
public abstract class AbstractExecutorService implements ExecutorService
-------------------------------------------------------------
public interface ExecutorService extends Executor

2)根据阿里的Java使用手册中指出是不允许直接使用JDK提供的默认线程池,必须使用自定义线程池,在使用线程池时我们经常用到的类是 ThreadPoolExecutor ,他有 6 个方法参数,每个参数是什么意思呢:

//JDK默认的线程池,这里只举例一个,还有其他的默认线程池,在阿里规范手册里都是不允许直接使用的
ExecutorService pool = Executors.newCachedThreadPool()
---------------------------------------------------------------------------------
//自定义线程池
//第一个 int 型参数为corePoolSize
//第二个 int 型参数为maximumPoolSize
//第三个 int 型参数为keepAliveTime
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(5), new ThreadPoolExecutor.DiscardOldestPolicy());

在选用 workQueue 时最好使用的是有界队列,如果使用无界队列,会造成以下影响:
1) corePoolSize 满时会加入到阻塞队列进行等待,而选用无界队列时,线程池中的数量将不可能超过 corePoolSize。因此 maximumPoolSize 这个参数无效。
2)由于线程池中的数量将不可能超过 corePoolSize,因此 keepAliveTime 这个参数也无效。
3)更重要一点的是,使用无界队列有可能会耗尽系统资源,而使用有界队列有利于防止系统资源耗尽,同时使队列处于一个合适大小的范围内。

线程池的工作机制

1)如果当前线程数 少于 核心线程数,则直接在线程池中创建线程执行任务(注意:执行这一步需要获取全局锁);
2)如果运行的线程大于核心线程数,则将任务加入到 BlockingQueue
3)如果无法加入到 BlockingQueue(队列已满),则创建新的线程来执行任务;
4)如果创建新线程使当前线程超过线程超过最大线程数 (maximumPoolSize) ,则会调用拒绝策略 (RejectedExecutionHandler) 来处理新线程,调用RejectedExecutionHandler.rejectedExecution()方法。

提交任务

在线程池中提交任务有 execute() 和 submit() 两个方法,那你知道两个方法的区别吗?

关闭线程池

在使用完线程池执行完任务后,要记得关闭线程池,否则线程池一直处于等待状态导致程序会一直运行。而关闭线程池也存在两个方法 shutdown() 和 shutdownNow()。它们的原理是调用线程的 interrupt() 方法来中断线程,而无法响应中断的线程可能永远无法停止。

    //工作线程
    static class Worker implements Runnable {

        private String taskName;
        private Random random = new Random();

        public Worker(String taskName) {
            this.taskName = taskName;
        }

        public String getTaskName() {
            return taskName;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()
                    + " process the task : " + taskName);
            SleepTools.ms(random.nextInt(100) * 5);
        }
    }

    static class CallWorker implements Callable<String> {

        private String taskName;
        private Random random = new Random();

        public CallWorker(String taskName) {
            this.taskName = taskName;
        }

        public String getTaskName() {
            return taskName;
        }

        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName()
                    + " process the task : " + taskName);
            SleepTools.ms(random.nextInt(100) * 5);
            return Thread.currentThread().getName() + ":" + random.nextInt(100) * 5;
        }
    }

    public static void main(String[] args) {
        //自定义线程池
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(5), new ThreadPoolExecutor.DiscardOldestPolicy());

        //系统自带的
//    ExecutorService pool = Executors.newCachedThreadPool()

        //执行execute
        for (int i = 0; i < 6; i++) {
            Worker worker = new Worker("Worker_"+i);
            pool.execute(worker);
        }

        //执行submit
        for (int i = 0;i<6;i++){
            CallWorker callWorker = new CallWorker("CallWorker_"+i);
            //获得任务执行完返回的 future
            Future<String> future = pool.submit(callWorker);
            try {
                //获取返回的结果
                String result = future.get();
                System.out.println(result);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        //使用完线程池后,记得关闭线程池,否则系统一直等待
        pool.shutdown();
//        pool.shutdownNow();
    }
}

致敬学习~

上一篇 下一篇

猜你喜欢

热点阅读