传统多线程带来的问题:
多线程运行时间,系统不断的创建,销毁线程带来额外的开销。过度切换线程的危险,从而导致系统资源的崩溃线程池的好处:
降低系统资源消耗,通过重用已存在的线程,降低线程创建的开销方便线程并发数的管控提供更强大的功能,延时定时线程池。池化技术:
不直接创建具体的资源,而是创建一个池,在池里面创建具体的资源。
以前是直接控制资源去执行任务,现在是把任务交给池,池就会让空闲的资源去执行任务,
任务完成后,资源不会被销毁,而是停留在 池里面,等待下一个任务。
在线程池的编程模式下。
任务是提交给整个线程池,线程池在拿到任务后,就在内部寻找空闲的线程执行任务。
一个线程只能执行一个任务,但可以同时向一个线程池提交多个线程池。
线程池工作的本质就是将要执行的任务添加到队列中,然后线程池寻找空闲的线程来执行队列中空闲的任务。
java中主要采用BlockingQueue去存储任务
双缓冲队列,内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提
高了队列的存储效率。
采用数组实现,规定大小,采用FIFO的顺序存储数据。
采用双向链表实现,大小不固定。若不指定大小,大小由Integer.MAX_VALUE指定,存储顺序为FIFO。
java中使用ThreadPoolExecutor来表示线程池,创建该类对象来表示创建一个线程池
new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
corePoolSize 核心线程数,创建线程池就会创建的线程数,核心线程不会被回收
maximumPoolSize 最大线程数,除了核心线程外还会存在临时线程,最大线程数是核心线程+临时线程的最大允许数。
keepAliveTime 线程池中除了核心线程之外的其他线程存活时间
unit 时间单位 TimeUnit中提供了几个枚举类 有SECONDS 、MINUTES、HOURS、DAYS
workQueue 工作队列 通常使用 SynchronousQueue。
实例:
new ThreadPoolExecutor(2,6,2, TimeUnit.SECONDS,new SynchronousQueue<>());通过线程池的execute方法执行任务
例子:
ublic static Boolean flag = true; public static void main(String[] args) { Runnable testA = new Runnable() { @Override public void run() { synchronized (Object.class){ for (int i=1;i<=100;i++) //flag为true时可以执行 if (flag){ System.out.println("A"+i); try { //任务执行完毕后 反转标志 并叫醒另一个线程,自己进入等待 flag=!flag; Object.class.notify();//争夺什么锁资源就在什么锁资源上等待或者唤醒 Object.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; Runnable testB = new Runnable() { @Override public void run() { synchronized (Object.class){ for (int i=1;i<=100;i++) if (!flag){ System.out.println("B"+i); try { flag=!flag; Object.class.notify(); Object.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; ThreadPoolExecutor ex= new ThreadPoolExecutor(4,5,6, TimeUnit.SECONDS, new LinkedBlockingDeque<>()); ex.execute(testA); ex.execute(testB);可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
例子:
//重复使用线程,只要线程空闲就不会创建新的线程,而是使用上一个任务遗留的线程去执行任务。 //线程池尽量不创建新的线程,只有迫不得已才会创建新的线程,最大线程数是Integer.MAX_VALUE(2147483647) //没有核心线程,等到60秒后进程会自动关闭 ExecutorService exe= Executors.newCachedThreadPool(); for (int i=0;i<20;i++){ //Thread.sleep(2); exe.execute(()-> System.out.println(Thread.currentThread().getName())); }创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
例子:
//输入参数是多少 创建的线程池的核心线程数量就是多少 //线程池一旦创建,就会创建指定size的核心线程,jvm不会自动关闭 //线程池的最大线程数也是核心线程数,不存在临时线程。 ExecutorService ex =Executors.newFixedThreadPool(5); for (int i=1;i<=20;i++){ ex.execute(()-> System.out.println(Thread.currentThread().getName())); } //查看电脑最大允许核心线程数,若超过则无法创建 System.out.println(Runtime.getRuntime().availableProcessors());创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
//线程池里面只创建了一个线程,这样就可以保证按顺序去执行任务 ExecutorService ex= Executors.newSingleThreadExecutor(); for (int i=1;i<=10;i++){ ex.execute(()-> System.out.println(Thread.currentThread().getName())); }创建一个定长线程池,支持定时及周期性任务执行
//增加了延迟执行和周期性执行 ScheduledExecutorService ex= Executors.newScheduledThreadPool(5); //延迟执行 //参数列表:Runnable command, long delay, TimeUnit unit ex.schedule(()-> System.out.println("hello"),3,TimeUnit.SECONDS); //周期性执行 第一个参数为Runnable,第二个参数为初始延迟,第三个重复执行间隔,第四个为时间单位 //参数列表Runnable command, long initialDelay, long period, TimeUnit unit ex.scheduleAtFixedRate(()-> System.out.println("world"),1,2,TimeUnit.SECONDS);