为什么需要线程池
- 因为如果不使用线程池的化,就需要每次使用线程的时候创建并销毁线程,造成了资源的浪费。而使用线程池对线程进行管理,线程只需要一次性创建并可以进行重复的使用,并且还可以根据需要调整线程池的大小来执行相应的方法。
JDK提供的线程池
线程池的使用
- 线程池的种类
- newFixedThreadPool
- 说明: 生成固定大小的线程池,执行任务时始终是这10个线程进行任务的执行。
- 结构:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
- newSingleThreadExecutor
- 说明:生成只有一个线程的线程执行器,不管如何执行任务都是单线程进行。
- 结构:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- newCacheedThreadPool
- 带缓冲的线程池,根据实际需要创建对应的数量的线程,长时间不用线程会进行自然的消亡,其最小的核心线程数为0。
- 结构:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
- newScheduledThreadPool
- 说明:对线程在自定义时间内进行定时执行。
- 结构:
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
- API:
public class ScheduledThreadPoolTest { public static void main(String[] args) { final ThreadA tda=new ThreadA(); final ThreadB tdb=new ThreadB(); final ThreadC tdc=new ThreadC(); ScheduledExecutorService exec=Executors.newScheduledThreadPool(5); System.out.println("开启延时任务执行,执行时间:"+getCurrentDate()); //在自定义时间后延时执行线程 exec.schedule(tda,1, TimeUnit.SECONDS); System.out.println("开启延时定时任务执行,执行时间:"+getCurrentDate()); //在自定义时间A后延时执行线程,并每隔自定义时间B后轮询执行线程。 exec.scheduleAtFixedRate(tdb,1,2,TimeUnit.SECONDS); System.out.println("开启延时(定时加线程执行时间)任务执行,执行时间:"+getCurrentDate()); //在自定义时间A后延时执行线程,并没隔自定义时间B+线程本身执行时间轮询执行线程。 exec.scheduleWithFixedDelay(tdc,1,2,TimeUnit.SECONDS); try { TimeUnit.SECONDS.sleep(15); exec.shutdown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static String getCurrentDate(){ Date date=new Date(); SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss"); return sdf.format(date); } } class ThreadA implements Runnable{ public void run() { System.out.println("ThreadA,执行时间:"+ScheduledThreadPoolTest.getCurrentDate()); } } class ThreadB implements Runnable{ public void run() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadB,执行时间:"+ScheduledThreadPoolTest.getCurrentDate()); } } class ThreadC implements Runnable{ public void run() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("ThreadC,执行时间:"+ScheduledThreadPoolTest.getCurrentDate()); } }
执行结果: 开启延时任务执行,执行时间:14:44:29 开启延时定时任务执行,执行时间:14:44:29 开启延时(定时加线程执行时间)任务执行,执行时间:14:44:29 ThreadA,执行时间:14:44:30 ThreadB,执行时间:14:44:33 ThreadC,执行时间:14:44:33 ThreadB,执行时间:14:44:36 ThreadC,执行时间:14:44:38 ThreadB,执行时间:14:44:39 ThreadB,执行时间:14:44:42 ThreadC,执行时间:14:44:43 ThreadB,执行时间:14:44:45
- newFixedThreadPool
线程池的共性
- 都是通过ThreadPoolExecutor进行线程池的创建而来;
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
- 说明:线程池的创建中需要一个corePoolSize(核心线程数,也就是最小线程数),maximumPoolSize(最大线程数),keepAliveTime(线程最大无响应时间,当线程没有使用的话就进行销毁,一直到只剩核心线程数大小为止),TimeUnit (时间单位),BlockingQueue(任务队列),而之后还有两个参数如果没有必要的情况下就会选择默认的第一个是线程的默认生产工厂defaultThreadFactory,之后的则是默认的拒绝策略defaultHandler,之后会讲到这个拒绝策略的详情。