Java线程:线程池 - 千百度社区-后端论坛-技术交流-千百度社区

Java线程:线程池

Java线程<第五篇>:线程池

utils包提供开了 ExecutorService 线程池的实现,主要目的是为了重复利用线程,提高系统效率。
Thread是一个重量级的资源,创建、启动以及销毁都是比较耗费系统资源的,因此使用线程池来管理线程是一个非常重要的编程习惯。

1、Thread
    new Thread(new Runnable() {
        @Override
        public void run() {

        }
    }).start();

直接使用 Thread 的弊端如下:

  • 每次new Thread新建对象性能差。
  • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
  • 缺乏更多功能,如定时执行、定期执行、线程中断。
2、线程池(ExecutorService、ThreadPool)
(1)newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        try {
            Thread.sleep(index * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(index);
            }
        });
    }
    cachedThreadPool.shutdown();

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

(2)newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

    ExecutorService cachedThreadPool = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 10; i++) {
        final int index = i;
        try {
            Thread.sleep(index * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(index);
            }
        });
    }
    cachedThreadPool.shutdown();

这里支持的最大线程数是5, 也可以根据系统而定,获取系统可被利用的进程数

Runtime.getRuntime().availableProcessors()
(3)newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

定义线程池,最大线程数是5

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

延迟执行:

    scheduledThreadPool.schedule(new Runnable() {

        @Override
        public void run() {
            System.out.println("delay");
        }
    }, 3, TimeUnit.SECONDS);

延迟1秒,并每隔3秒定期执行

    scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

        @Override
        public void run() {
            System.out.println("delay 1 seconds, and excute every 3 seconds");
        }
    }, 1, 3, TimeUnit.SECONDS);

关于延迟执行和周期性执行我们还会想到Timer

    Timer timer = new Timer();
    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            
        }
    };
    timer.schedule(timerTask, 1000, 3000);
(4)newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        final int index = i;
        singleThreadExecutor.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    System.out.println(index);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

Java提供的四种线程池的优点

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、定期执行、单线程、并发数控制等功能。
(5)自定义线程池

如果我们不想使用以上4种线程池,可以自定义一个线程池:

/**
 * 线程池
 */
public class DefaultPoolExecutor {

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {

        private final AtomicInteger mCount = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable runnable) {
            // 线程
            return new Thread(runnable, "ThreadName #" + mCount.getAndIncrement());
        }
    };


    // 可用处理器的Java虚拟机的数量
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // 最大线程数(最佳线程数 = CPU_COUNT + 1)
    private static final int MAX_CORE_POOL_SIZE = CPU_COUNT + 1;
    //空闲时间达到 30s 时,回收空闲线程(每隔30s回收一次)
    private static final long THREAD_TIMEOUT = 30L;

    /**
     * 新建一个线程池
     * 每个线程都会消耗大概1M的内存,使用线程池管理和复用线程
     *
     * @param corePoolSize 线程池大小
     * @return
     */
    public static ThreadPoolExecutor newDefaultPoolExecutor(int corePoolSize) {
        if (corePoolSize == 0) {
            return null;
        }
        corePoolSize = Math.min(corePoolSize, MAX_CORE_POOL_SIZE);
        int maximumPoolSize = corePoolSize;
        // corePoolSize: 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程
        // 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
        // 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
        // 当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
        // 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
        // 当设置allowCoreThreadTimeOut(true)时,线程池中线程空闲时间达到keepAliveTime也将关闭
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, THREAD_TIMEOUT,
            TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(64), sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        return threadPoolExecutor;
    }

}

扩充:

除了execute可以执行线程池中的线程之外,submit也是可以的

图片.png

submit的效果和execute是一样的,只是execute没有返回值,而submit有返回值。

(1)Future<?> submit(Runnable task)
        Future future1 = singleThreadExecutor.submit(new Runnable() {
            @Override
            public void run() {

            }
        });
(2)<T> Future<T> submit(Runnable task, T result)
        Future<String> future = singleThreadExecutor.submit(new Runnable() {
            @Override
            public void run() {

            }
        }, "A");
(3)<T> Future<T> submit(Callable<T> task);
    Callable callable = new Callable<String>() {

        @Override
        public String call() throws Exception {
            return "A";
        }
    };

    Future<String> future = singleThreadExecutor.submit(callable);

如果Future中有值的话可以通过以下代码获取

        try {
            System.out.println(future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

Future的其余操作如图

图片.png

[本章完…]

© 著作权归作者所有,转载或内容合作请联系作者

请登录后发表评论

    没有回复内容