美文网首页
面试题一天一题——第七天 · (线程池的使用及其原理)

面试题一天一题——第七天 · (线程池的使用及其原理)

作者: 设计失 | 来源:发表于2018-10-12 16:40 被阅读61次

线程池的优点

  • 避免创建线程时反复的创建对象而消耗内存
  • 对多个线程进行统一的管理,避免线程中资源的竞争
  • 使用java自带的线程池,方便简单

在使用new Thread的形式创建线程时,阿里的规范推荐我们使用new ThreadPoolExcutor的方式显示的在代码中写出核心线程、最大线程和非核心线程在线程池中等待的时间等参数:

阿里检测不建议直接new Thread使用线程.png

Java 常用的四种线程池

/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available.  These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* 缓存线程池
* 非必要的时候会重用之前创建的线程从而达到缓存复用的目的, 如果一个线程在60秒 
*之后没有被利用则会从缓存池中移除,所以长时间闲置的线程不会消耗任何资源!
*/
public static ExecutorService newCachedThreadPool() {
   return new ThreadPoolExecutor(/*corePoolSize*/0, /*maximumPoolSize*/Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue, and uses the provided ThreadFactory to
* create a new thread when needed. Unlike the otherwise
* equivalent {@code newFixedThreadPool(1, threadFactory)} the
* returned executor is guaranteed not to be reconfigurable to use
* additional threads.
*
*  创建一个使用单工作线程的线程池
*
*/
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
   return new FinalizableDelegatedExecutorService
       (new ThreadPoolExecutor(/*corePoolSize*/1, /*maximumPoolSize*/1,
                               0L, TimeUnit.MILLISECONDS,
                               new LinkedBlockingQueue<Runnable>(),
                               threadFactory));
}

/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue, using the provided
* ThreadFactory to create new threads when needed.  At any point,
* at most {@code nThreads} threads will be active processing
* tasks.  If additional tasks are submitted when all threads are
* active, they will wait in the queue until a thread is
* available.  If any thread terminates due to a failure during
* execution prior to shutdown, a new one will take its place if
* needed to execute subsequent tasks.  The threads in the pool will
* exist until it is explicitly {@link ExecutorService#shutdown
* shutdown}.
*
* 创建一个核心线程数和最大线程数相同的线程池
* 
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
   return new ThreadPoolExecutor(/*corePoolSize*/nThreads, /*maximumPoolSize*/nThreads,
                                 0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>(),
                                 threadFactory);
}

/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* 
* 创建一个线程池,在运行一个线程之后可以给定延迟,或定期执行
*/
public static ScheduledExecutorService newScheduledThreadPool(
       int corePoolSize, ThreadFactory threadFactory) {
   return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

public ScheduledThreadPoolExecutor(int corePoolSize,
                                  ThreadFactory threadFactory) {
   super(corePoolSize, Integer.MAX_VALUE,
         DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
         new DelayedWorkQueue(), threadFactory);
}

CachedThreadPool 缓存线程池的使用

  • 该线程池没有核心线程,只有非核心线程,使用SynchronousQueue来管理线程队列
 // 缓存线程
ExecutorService executorService = Executors.newCachedThreadPool();
        // 等同于
//        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
//                60L, TimeUnit.SECONDS,
//                new SynchronousQueue<Runnable>());

for (int i = 0; i < 10; i++) {
    final int j = i;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2_000);
                Log.d(TAG, "当前线程为:" + Thread.currentThread().getName() + " 当前值为:" + j);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    executorService.execute(runnable);
}

// 运行结果
10-12 15:35:14.593 22819-22819/com.ellison.aidl D/TestThreadPool: ==== 进入到onCreate()
10-12 15:35:16.594 22819-22842/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-2 当前值为:1
10-12 15:35:16.594 22819-22841/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-1 当前值为:0
10-12 15:35:16.594 22819-22843/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-3 当前值为:2
10-12 15:35:16.594 22819-22844/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-4 当前值为:3
10-12 15:35:16.594 22819-22845/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-5 当前值为:4
10-12 15:35:16.595 22819-22846/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-6 当前值为:5
10-12 15:35:16.595 22819-22847/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-7 当前值为:6
10-12 15:35:16.595 22819-22848/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-8 当前值为:7
10-12 15:35:16.595 22819-22849/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-9 当前值为:8
10-12 15:35:16.595 22819-22850/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-1-thread-10 当前值为:9
  • 因为SynchronousQueue是不存储元素的,每一个的put都伴随着transfer取出元素操作
  • 从运行结果来看,该线程池在短时间内创建了很多线程来执行任务,这是因为没有核心线程的原因,而且非核心线程都在执行任务,所以会快速的创建新的线程来执行任务
  • 所以,从上面的结果我们可以看出缓存线程池适用于耗时少而有大量任务的情况

SingleThreadExecutor 单个线程池的使用

由于核心线程数和最大线程数都为1,所以在线程池中有任务进行时,其他任务都需要等待执行完之后才能执行
// 单个线程
ExecutorService singleService = Executors.newSingleThreadExecutor();
        // 等同于
//        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(/*corePoolSize*/1, /*maximumPoolSize*/1,
//                0L, TimeUnit.MILLISECONDS,
//                new LinkedBlockingQueue<Runnable>());

for (int i = 0; i < 10; i++) {
    final int j = i;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2_000);
                Log.d(TAG, "==== 当前线程为:" + Thread.currentThread().getName() + " 当前值为:" + j);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    singleService.execute(runnable);
}

// 运行结果为:
10-12 16:16:36.487 25343-25343/com.ellison.aidl D/TestThreadPool: ==== 进入到onCreate()
10-12 16:16:38.490 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:0
10-12 16:16:40.490 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:1
10-12 16:16:42.491 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:2
10-12 16:16:44.491 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:3
10-12 16:16:46.492 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:4
10-12 16:16:48.493 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:5
10-12 16:16:50.493 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:6
10-12 16:16:52.494 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:7
10-12 16:16:54.494 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:8
10-12 16:16:56.495 25343-25417/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-2-thread-1 当前值为:9
  • 只有一个线程的线程池,存活时间为0是无限的,
  • 适用于单个任务执行的场景

FixedThreadPool 固定线程线程池使用

// 固定线程
ExecutorService fixService = Executors.newFixedThreadPool(3);
        // 等同于
//        ThreadPoolExecutor fixService = new ThreadPoolExecutor(3, 3,
//                0L, TimeUnit.MILLISECONDS,
//                new LinkedBlockingQueue<Runnable>());

for (int i = 0; i < 10; i++) {
    final int j = i;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2_000);
                Log.d(TAG, "==== 当前线程为:" + Thread.currentThread().getName() + " 当前值为:" + j);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    fixService .execute(runnable);
}

// 运行结果:
10-12 16:22:01.308 26019-26019/com.ellison.aidl D/TestThreadPool: ==== 进入到onCreate()
10-12 16:22:03.309 26019-26045/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-2 当前值为:1
10-12 16:22:03.309 26019-26044/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-1 当前值为:0
10-12 16:22:03.309 26019-26046/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-3 当前值为:2
10-12 16:22:05.310 26019-26045/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-2 当前值为:5
10-12 16:22:05.310 26019-26046/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-3 当前值为:3
10-12 16:22:05.310 26019-26044/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-1 当前值为:4
10-12 16:22:07.310 26019-26046/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-3 当前值为:7
10-12 16:22:07.310 26019-26045/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-2 当前值为:6
10-12 16:22:07.310 26019-26044/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-1 当前值为:8
10-12 16:22:09.311 26019-26046/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-3-thread-3 当前值为:9
  • 固定线程中核心线程数和最大线程数是相同的
  • 当线程池中线程队列满了的时候,会处于等待状态,同时线程会无序运行
  • 适用于长期的任务

ScheduledThreadPool 可调度线程池

// 可调度线程
ScheduledExecutorServicescheduleService = Executors.newScheduledThreadPool(3);
        // 等同于
//         public ScheduledThreadPoolExecutor(int corePoolSize) {
//            super(corePoolSize, Integer.MAX_VALUE,
//                    DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
//                    new DelayedWorkQueue());
//        }
for (int i = 0; i < 10; i++) {
    final int j = i;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                  //Thread.sleep(2_000);
                Log.d(TAG, "==== 当前线程为:" + Thread.currentThread().getName() + " 当前值为:" + j);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
      // fixService.execute(runnable);
      // 延时1秒执行
     scheduleService.schedule(runnable, 1_000, TimeUnit.MILLISECONDS);
// 延时1秒启动,后面的任务也延时1秒执行
//scheduleService.scheduleAtFixedRate(runnable, 1_000,1_000, TimeUnit.MILLISECONDS);
}

// 运行结果为:
10-12 16:32:09.360 27537-27537/com.ellison.aidl D/TestThreadPool: ==== 进入到onCreate()
10-12 16:32:10.361 27537-27558/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-1 当前值为:0
10-12 16:32:10.365 27537-27559/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-2 当前值为:1
10-12 16:32:10.366 27537-27560/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-3 当前值为:2
10-12 16:32:10.366 27537-27558/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-1 当前值为:3
10-12 16:32:10.366 27537-27559/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-2 当前值为:4
    ==== 当前线程为:pool-4-thread-2 当前值为:6
10-12 16:32:10.366 27537-27560/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-3 当前值为:5
10-12 16:32:10.366 27537-27559/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-2 当前值为:7
    ==== 当前线程为:pool-4-thread-2 当前值为:9
10-12 16:32:10.366 27537-27558/com.ellison.aidl D/TestThreadPool: ==== 当前线程为:pool-4-thread-1 当前值为:8
  • 核心线程数为固定的,但是非核心线程数无限制(Integer.MAX_VALUE)
  • 适用于周期性执行任务的场景

相关文章

网友评论

      本文标题:面试题一天一题——第七天 · (线程池的使用及其原理)

      本文链接:https://www.haomeiwen.com/subject/wmabaftx.html