本系列为笔者学习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{
@Override
public 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{
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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();
}
}
}
}
1007

被折叠的 条评论
为什么被折叠?



