Javase 基础加强 —— 11 线程池
本系列为笔者学习Javase的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员Java+AI智能辅助编程全套视频教程,java零基础入门到大牛一套通关》,章节分布参考视频教程,为同样学习Javase系列课程的同学们提供参考。
01 概述
用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。
那么这时,线程池就是一个可以复用线程的技术。
02 创建线程池
JDK5.0
起提供了代表线程池的接口:ExecutorService / ɪɡˈzekjʊtər /
。
如何创建线程池对象?
① 使用ExecutorService
的实现类ThreadPoolExecutor
自创建一个线程池对象。
ExecutorService pool = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
② 使用Executors
(线程池的工具类)调用方法返回不同特点的线程池对象。
03 处理 Runnable 任务
ExecutorService
的常用方法
MyRunnable.java
package ExecutorDemo;public class MyRunnable implements Runnable{@Overridepublic void run(){for(int i=0; i<5; i++){System.out.println(Thread.currentThread().getName());}}
}
ExecutorServiceDemo1.java
package ExecutorDemo;import java.util.concurrent.*;public class ExecutorServiceDemo1 {public static void main(String[] args) {//目标:创建线程池对象//1.使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//2.使用线程池处理Runnable任务Runnable target = new MyRunnable();pool.execute(target); //提交第1个任务 创建第1个线程 自动启动线程处理这个任务pool.execute(target); //提交第1个任务 创建第1个线程 自动启动线程处理这个任务pool.execute(target); //提交第1个任务 创建第1个线程 自动启动线程处理这个任务pool.execute(target); //复用线程pool.execute(target); //复用线程//3.关闭线程池//pool.shutdown();}
}
问:什么时候开始创建临时线程?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时会创建临时线程。
问:什么时候会拒绝新任务?
新任务提交时发现核心线程和临时线程都在忙,任务队列也满了,此时会拒绝任务。
MyRunnable.java
package ExecutorDemo;public class MyRunnable implements Runnable{@Overridepublic void run(){for(int i=0; i<5; i++){System.out.println(Thread.currentThread().getName() + "输出" + i);try {Thread.sleep(Integer.MAX_VALUE);} catch (Exception e) {e.printStackTrace();}}}
}
ExecutorServiceDemo1.java
package ExecutorDemo;import java.util.concurrent.*;public class ExecutorServiceDemo1 {public static void main(String[] args) {//目标:创建线程池对象//1.使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());//2.使用线程池处理Runnable任务Runnable target = new MyRunnable();pool.execute(target); //召唤第1个正式工pool.execute(target); //召唤第2个正式工pool.execute(target); //召唤第3个正式工pool.execute(target); //门口等待1位pool.execute(target); //门口等待2位pool.execute(target); //门口等待3位pool.execute(target); //招聘第1个临时工pool.execute(target); //招聘第2个临时工pool.execute(target); //老板亲自服务//3.关闭线程池//pool.shutdown();}
}
04 处理 Callable 任务
ExecutorService
的常用方法
MyCallable.java
package ExecutorDemo;import java.util.concurrent.Callable;public class MyCallable implements Callable<String> {private int n;public MyCallable(int n){this.n = n;}@Overridepublic String call() throws Exception{int sum = 0;for(int i=1; i<=n; i++){sum += i;}return Thread.currentThread().getName() + "子线程" + n + "计算sum为:" + sum;}
}
ExecutorServiceDemo2.java
package ExecutorDemo;import java.util.concurrent.*;public class ExecutorServiceDemo2 {public static void main(String[] args) {//目标:创建线程池对象//1.使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象ExecutorService pool = new ThreadPoolExecutor(3, 5, 10, TimeUnit.SECONDS,new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());//2.使用线程池处理Callable任务Future<String> f1 = pool.submit(new MyCallable(100));Future<String> f2 = pool.submit(new MyCallable(200));Future<String> f3 = pool.submit(new MyCallable(300));Future<String> f4 = pool.submit(new MyCallable(400));try {System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());} catch (Exception e) {e.printStackTrace();}}
}
05 通过 Executors 创建新线程
Executors 是一个线程池的工具类,其提供了很多静态方法用于返回不同特点的线程池对象。
ExecutorService pool Executors.newFixedThreadPool(3);
06 并发/并行
当前正在运行的程序为一个独立的进程,而线程是属于进程的,一个进程中可以同时运行很多个线程。
进程中的线程是由CPU
负责调度执行的,但CPU
能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU
会轮询为系统的每个线程服务,由于CPU
切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并发是虚假的同时,并行是真正的同时。
07 案例:红包雨游戏
需求:某企业有100名员工,员工的工号依次是1,2,3,4,…,100。现在公司举办了年会活动,活动中有一个红包雨环节,要求共计发出200个红包雨。其中小红包在1-30元之间,总占比为80%,大红包[31-100】元,总占比为20%。
分析:100个员工实际上就是100个线程,来竞争200个红包。
Test.java
package threadDemo;import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class Test {public static void main(String[] args) {List<Integer> redPacket = getRedPacket();//2.定义线程类,创建100个线程,竞争200个红包for(int i=1; i<=100; i++){new PeopleGetRedPacket(redPacket, i + "号程序员");}}public static List<Integer> getRedPacket(){Random r = new Random();//1.创建List集合,准备200个随机红包List<Integer> redPacket = new ArrayList<>();for(int i=1; i<=160; i++){redPacket.add(r.nextInt(30) + 1);}for(int i=1; i<=40; i++){redPacket.add(r.nextInt(70) + 31);}return redPacket;}
}
PeopleGetRedPacket.java
package threadDemo;import java.util.List;public class PeopleGetRedPacket extends Thread{//构造函数立大功!!!private List<Integer> redPacket;public PeopleGetRedPacket(List<Integer> redPacket, String name){super(name);this.redPacket = redPacket;}@Overridepublic void run(){//3.创建加锁机制,模拟抢红包过程String name = Thread.currentThread().getName();while(true){if(redPacket.size() == 0){break;}int index = (int)(Math.random() * redPacket.size());Integer money = redPacket.remove(index);System.out.println(name + "抢到了" + money + "元");if(redPacket.size() == 0){System.out.println("活动结束!");break;}try{Thread.sleep(10);}catch (Exception e){e.printStackTrace();}}}
}