当有多个线程在运行时,如果系统只有一个CPU,那么它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,在将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态,这种方式我们称之为并发(Concurrent)。
1.线程是稀缺资源,不能频繁的创建
2.解耦作用,线程的创建与执行完全分开,方便维护
3.一个任务结束后,断开与线程池的连接,可以给其他任务复用。
核心的思想就是把宝贵的资源放到一个池子中去,每次使用都从池子中获取,用完之后又放回池子供他人使用。
如果没吃使用new Thread()来创建线程,不仅耗费性能,而且创建的线程缺乏管理,被称为野线程,而且还可以无限制的创建,导致相互竞争,过多占用系统资源导致系统崩溃。如果采用线程池的方法,不仅可以减少对象的创建,节省性能,而且还可以重用存在的线程,还可以有效控制最大并发线程数,提高系统资源使用率,同时也避免了过多资源竞争,避免堵塞。
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务的线程相当于消费者,并用Runnable来表示任务,Executor的实现还提供了对生命周期的支持,以及统计信息收集,应用程序管理机制和性能监视等机制。
1 /** 2 * 创建任务对象,实现Runnable接口 3 * @author Administrator 4 * 5 */ 6 public class TaskDemo implements Runnable { 7 8 @Override 9 public void run() { 10 try { 11 System.out.println(Thread.currentThread().getName()+" is running!"); 12 TimeUnit.SECONDS.sleep(2); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 } 17 18 }
1 /** 2 * 固定大小的线程池 3 * @author Administrator 4 * 5 */ 6 public class FixPollDemo { 7 8 public static void main(String[] args) { 9 //创建固定大小的线程池 10 ExecutorService pool = Executors.newFixedThreadPool(5); 11 //创建10个线程任务给线程池 12 for (int i = 0; i < 10; i++) { 13 //创建线程任务 14 Runnable task = new TaskDemo(); 15 //把任务交给线程池去执行 16 pool.execute(task); 17 } 18 //关闭线程池 19 pool.shutdown(); 20 } 21 22 }
1 /** 2 * 创建可变大小的线程池 3 * @author Administrator 4 * 5 */ 6 public class CachePollDemo { 7 8 public static void main(String[] args) { 9 //创建可变大小的线程池 10 ExecutorService cachePool = Executors.newCachedThreadPool(); 11 //创建10个线程任务给线程池 12 for (int i = 0; i < 10; i++) { 13 //创建线程任务 14 Runnable task = new TaskDemo(); 15 //把任务交给线程池去执行 16 cachePool.execute(task); 17 } 18 //关闭线程池 19 cachePool.shutdown(); 20 } 21 }
1 /** 2 * 单线程线程池 3 * @author Administrator 4 * 5 */ 6 public class SinglePollDemo { 7 8 public static void main(String[] args) { 9 //创建线程池 10 ExecutorService singlePool = Executors.newSingleThreadExecutor(); 11 //创建10个线程任务给线程池 12 for (int i = 0; i < 10; i++) { 13 //创建线程任务 14 Runnable task = new TaskDemo(); 15 //把任务交给线程池去执行 16 singlePool.execute(task); 17 } 18 //关闭线程池 19 singlePool.shutdown(); 20 } 21 }
1 /** 2 * 可调度的线程池 3 * @author Administrator 4 * 5 */ 6 public class SchedulePollDemo { 7 8 public static void main(String[] args) { 9 //创建可调度的线程池 10 ExecutorService schedulePool = Executors.newScheduledThreadPool(5); 11 //创建10个线程任务给线程池 12 for (int i = 0; i < 10; i++) { 13 //创建线程任务 14 Runnable task = new TaskDemo(); 15 //把任务交给线程池去执行 16 schedulePool.execute(task); 17 } 18 //关闭线程池 19 schedulePool.shutdown(); 20 } 21 22 }
查看代码会发现,其实看这三种方式创建的源码就会发现,以上三种都是利用利用 ThreadPoolExecutor 类实现的。
1 /** 2 * 3 * @param corePoolSize 为线程池的基本大小。 4 * @param maximumPoolSize 为线程池最大线程大小。 5 * @param keepAliveTime 和 unit 则是线程空闲后的存活时间。 6 * @param workQueue 用于存放任务的阻塞队列。 7 * handler 当队列和最大线程池都满了之后的饱和策略。 8 */ 9 public ThreadPoolExecutor(int corePoolSize, 10 int maximumPoolSize, 11 long keepAliveTime, 12 TimeUnit unit, 13 BlockingQueue<Runnable> workQueue) { 14 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 15 Executors.defaultThreadFactory(), defaultHandler); 16 }
我们使用threadPool.execute(new Job())来提交一个任务到线程池,所有核心逻辑在execute()方法中。
1 public void execute(Runnable command) { 2 if (command == null) 3 throw new NullPointerException(); 4 // 获取当前线程池的状态 5 int c = ctl.get(); 6 // 当前线程数量小于 coreSize 时创建一个新的线程运行 7 if (workerCountOf(c) < corePoolSize) { 8 if (addWorker(command, true)) 9 return; 10 c = ctl.get(); 11 } 12 // 如果当前线程处于运行状态,并且写入阻塞队列成功 13 if (isRunning(c) && workQueue.offer(command)) { 14 int recheck = ctl.get(); 15 // 双重检查,再次获取线程状态;如果线程状态变了(非运行状态)就需要从阻塞队列移除任务, 16 // 并尝试判断线程是否全部执行完毕。同时执行拒绝策略。 17 if (!isRunning(recheck) && remove(command)) 18 reject(command); 19 else if (workerCountOf(recheck) == 0) // 如果当前线程池为空就新创建一个线程并执行。 20 addWorker(null, false); 21 } else if (!addWorker(command, false)) // 如果在第三步的判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略 22 reject(command); 23 }
线程池中所定义的状态。
1 private static final int RUNNING = -1 << COUNT_BITS; 2 private static final int SHUTDOWN = 0 << COUNT_BITS; 3 private static final int STOP = 1 << COUNT_BITS; 4 private static final int TIDYING = 2 << COUNT_BITS; 5 private static final int TERMINATED = 3 << COUNT_BITS;
状态说明:
RUNNING:
(1):状态说明:运行状态,指可以接受任务执行队列里的任务,线程池的初始化状态就是RUNNING,也就是说,线程池一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0;
(2):状态切换:线程池的初始化状态
SHUTDOWN:
(1):状态说明:线程池处于SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(2):状态切换:调用线程池的shutdown()方法时,线程池状态由RUNNING-->SHUTDOWN。
STOP:
(1):状态说明:线程池处于STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(2):状态切换:调用线程池的shutdownNow()方法时,线程池状态由(RUNNING 或 SHUTDOWN)-->STOP。
TIDYING:
(1):状态说明:当所有的任务已经终止,任务数量为0时,线程池状态会变为TIDYING状态,当线程池状态变为TIDYING时,会执行钩子函数terminated()。terminated()函数在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING状态时进行相应的处理,可以通过重载terminated()方法来实现。
(2):状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由SHUTDOWN-->TIDYING,当线程池在STOP状态下,线程池中的执行任务为空时,就会由STOP-->TIDYING。
TERMINATED:
终止状态,当执行terminated()方法时就会更新为这个状态。
(1):状态说明:线程池彻底终止,就会变成TERMINATED状态。
(2):状态切换:线程池处于TIDYING状态时,执行terminated()之后,状态由TIDYING-->TERMINATED。
通常我们是需要根据这批任务执行的性质来决定:
1.IO密集型任务:由于线程并不是一直都在运行,所以可以尽可能的多配置线程,比如CPU个数*2;
2.CPU密集型任务:(大量复杂的运算)应当分配较少的线程,比如CPU个数相当的大小。
其实无非就是两个方法shutdown()和shutdownNow();
shutdown()执行后,停止接收新任务,会把队列的任务执行完毕。
shutdownNow()也是停止接收新任务,但会中断所有的任务,将线程状态变为STOP。
原文:https://www.cnblogs.com/wk-missQ1/p/12384443.html