JAVA多线程技术-线程池创建方式和原理

1.通过Executors创建

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

下面这张图完整描述了线程池的类体系结构。

img

**Executor:**一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command),

**ExecutorService:**是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法

**AbstractExecutorService:**ExecutorService执行方法的默认实现

ScheduledExecutorService:一个可定时调度任务的接口

ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池

**ThreadPoolExecutor:**线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象

(1)创建缓存线程池

Executors.newCacheThreadPool():可缓存线程池,先查看线程池中有没有之前创建的线程,如果有则直接使用。否则就新创建一个新的线程加入线程池中,我们经常用此类线程池来执行一些业务处理时间很短的任务。

源码

/**
     * 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.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available, and uses the provided
     * ThreadFactory to create new threads when needed.
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

通过源码可以看出是对ThreadPoolExecutor封装。阻塞队列不装线程,最大线程数是Integer最大值,可以不断创建线程。

注意线程太多会发生OOM

代码实现

//缓存线程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i=0;i<10;i++) {
            //模拟线程调用的时间间隔
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            cachedThreadPool.submit(()->{
                //模拟业务处理时间
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("newCachedThreadPool创建线程池" + " " + Thread.currentThread().getName());
            });
        }

结果

newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-1

调大业务处理时间Thread.sleep(1000);,由于新创建的线程没有空闲,所以会一直创建新的线程,执行结果如下:

newCachedThreadPool创建线程池 pool-1-thread-1
newCachedThreadPool创建线程池 pool-1-thread-2
newCachedThreadPool创建线程池 pool-1-thread-3
newCachedThreadPool创建线程池 pool-1-thread-4
newCachedThreadPool创建线程池 pool-1-thread-5
newCachedThreadPool创建线程池 pool-1-thread-6
newCachedThreadPool创建线程池 pool-1-thread-7
newCachedThreadPool创建线程池 pool-1-thread-8
newCachedThreadPool创建线程池 pool-1-thread-9
newCachedThreadPool创建线程池 pool-1-thread-10

(2)创建固定长度线程池

Executors.newFixedThreadPool():创建一个固定(指定)长度可重用的线程池,可以控制最大创建数,超过最大长度之后就会放入到队列进行等待。最大线程数=核心线程数。

源码

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

源码可以看出只是留了一个传线程数的口子,最大线程数=核心线程数。

代码实现

//固定线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
for (int i=0;i<10;i++) {
  //模拟线程调用的时间间隔
  try {
    Thread.sleep(100);
  } catch (InterruptedException e) {
    e.printStackTrace();
  }

  newFixedThreadPool.submit(()->{
    //模拟业务处理时间
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("newFixedThreadPool创建线程池" + " " + Thread.currentThread().getName());
  });
}

结果

newFixedThreadPool创建线程池 pool-1-thread-1
newFixedThreadPool创建线程池 pool-1-thread-2
newFixedThreadPool创建线程池 pool-1-thread-1
newFixedThreadPool创建线程池 pool-1-thread-2
newFixedThreadPool创建线程池 pool-1-thread-1
newFixedThreadPool创建线程池 pool-1-thread-2
newFixedThreadPool创建线程池 pool-1-thread-1
newFixedThreadPool创建线程池 pool-1-thread-2
newFixedThreadPool创建线程池 pool-1-thread-1
newFixedThreadPool创建线程池 pool-1-thread-2

创建了两个线程的固定线程池,线程会进入等待队列,等有空闲线程再执行。使用LinkedBlockingQueue大小为Integer.MAX_VALUE。注意队列里放太多任务也会发生OOM的

(3)定时执行的线程池

Executors.newScheduledThreadPool():创建一个支持定时或周期性的执行任务线程池,可以延时执行,可以用做执行定时任务的线程池。

源码

  public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

定时线程池是根据延时队列来实现的。

代码实现

//可定时执行的线程池
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd hh:mm:ss");
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2);
//参数依次为:执行线程、初始化延时、线程调用时间间隔、计时单位
newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
  @Override
  public void run() {
    System.out.println("newScheduledThreadPool创建线程池" + " " + Thread.currentThread().getName() + "执行时间" + simpleDateFormat.format(new Date()));
  }
},5,10, TimeUnit.SECONDS);

结果

newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:04
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:14
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:24
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:34
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:44
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:02:54
newScheduledThreadPool创建线程池 pool-1-thread-1执行时间22-07-19 11:03:04

第一个线程延时5s,线程间执行间隔10s。

scheduleAtFixedRate和scheduleWithFixedDelay区别

img

假设线程耗时1s,定时执行3s,则scheduleWithFixedDelay其实是4s,

scheduleAtFixedRate:固定周期运行。

scheduleWithFixedDelay:任务结束时间周期进行。

(4)单线程化线程池

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

源码

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

最大线程数=核心线程数=1

代码实现

//单线程池
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i=0;i<10;i++) {
//模拟线程调用的时间间隔
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}

newSingleThreadExecutor.submit(()->{
//模拟业务处理时间
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("newSingleThreadExecutor创建线程池" + " " + Thread.currentThread().getName());
});
}

结果

newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1
newSingleThreadExecutor创建线程池 pool-1-thread-1

执行代码会看到同一时间只有一个线程执行,其余的任务都会有序等待且有序的执行。

2.自己创建

执行代码

//ThreadFactory参数,可以通过实现自己手动写一个实现ThreadFactory的类作为参数放到这里
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60L,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {   
                return new Thread(r,"自定义线程池"+r.hashCode());
            }
        });

        for (int i=0;i<10;i++) {
            //模拟线程调用的时间间隔
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            threadPoolExecutor.submit(()->{
                //模拟业务处理时间
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("newCachedThreadPool创建线程池" + " " + Thread.currentThread().getName());
            });
        }
    }

结果

newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362
newCachedThreadPool创建线程池 自定义线程池445884362

注意:以上线程池用完记得关闭。通过调用线程池的 shutdown 或 shutdownNow 方法来关闭线程池。

Executor和Executors区别

**Executor:**一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)。

Executors:是对线程池的包装,前面讲到了前三种线程池的创建。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值