1. 线程通信
1.1 线程通信概述
1.2 线程案例模拟
1.3 线程通信总结
2. 线程池(重点)
2.1 线程池概述
2.2 线程池实现的API,参数说明
2.2.1 参数说明
- 指定线程池的最大的核心线程数,创建线程池需要设定
- 线程池支持的最大线程数:核心线程+临时线程
- 指定临时线程的最大存活时间
- 指定临时线程存活时间的单位
- 指定任务队列:当核心线程都处于繁忙状态时,指定任务队列的大小,当请求的任务超过任务队列最大的大小时,才会创建临时线程
- 指定使用哪个线程工厂进行创建线程
- 指定核心线程和临时线程都忙,任务队列满时,新任务来了的解决方案
2.2.2 线程池常见面试题
2.2.3 总结
2.3 线程池处理Runnable任务
2.3.1 创建线程池示例
在创建线程池的最后一个参数我们介绍了当所有线程忙,任务队列满的时候,新任务来了怎么办
这里列举了四种拒绝策略
2.3.2 代码演示
- 创建一个Runnable任务对象
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "输出了:HelloWorld ==> " + i);
}
try {
System.out.println(Thread.currentThread().getName() + "本任务与线程绑定了,线程进入休眠了~~~");
Thread.sleep(10000000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 创建一个线程池, 调用线程池的execute方法执行任务
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 1、创建线程池对象
/**
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
*/
ExecutorService pool = new ThreadPoolExecutor(3, 5 ,
6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5) , Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() );
// 2、给任务线程池处理。
Runnable target = new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
// 创建临时线程
pool.execute(target);
pool.execute(target);
// // 不创建,拒绝策略被触发!!!
// pool.execute(target);
// 关闭线程池(开发中一般不会使用)。
// pool.shutdownNow(); // 立即关闭,即使任务没有完成,会丢失任务的!
pool.shutdown(); // 会等待全部任务执行完毕之后再关闭(建议使用的)
}
}
创建3个任务的运行结果
创建8个任务的运行结果
创建十个线程任务的结果
创建十一个线程的结果
总结一下:
我们创建的线程池核心线程为3,最大线程为5,也就是临时线程数为2,任务队列为5,因此,当我们创建3个任务时,核心线程帮我们执行。
当我们创建8个任务时,消息队列刚好满,这时候会等待核心线程处理完毕任务再从消息队列中获取任务。
当我们创建十个任务时,总任务数超过核心线程和消息队列的总和,这时候会创建临时线程来执行任务。
当创建十一个任务时,总任务数超过核心线程+临时线程+消息队列可容纳的数量,因此会触发任务拒绝策略。
2.3.3 总结
2.4 线程池处理Callable任务
2.4.1 执行Callable任务方法
2.4.2 代码演示
- 创建Callable任务
public class MyCallable implements Callable<String>{
private int n;
public MyCallable(int n) {
this.n = n;
}
/**
2、重写call方法(任务方法)
*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <= n ; i++) {
sum += i;
}
return Thread.currentThread().getName()
+ "执行 1-" + n+ "的和,结果是:" + sum;
}
}
- 创建线程池执行Callable任务
public class ThreadPoolDemo2 {
public static void main(String[] args) throws Exception {
// 1、创建线程池对象
/**
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
*/
ExecutorService pool = new ThreadPoolExecutor(3, 5 ,
6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5) , Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() );
// 2、给任务线程池处理。
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));
Future<String> f5 = pool.submit(new MyCallable(500));
// String rs = f1.get();
// System.out.println(rs);
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
System.out.println(f5.get());
}
}
执行结果:这里调用get方法是因为get方法返回的是线程执行完毕后的结果
2.4.3 总结
2.5 Executors工具类实现线程池
2.5.1 常用方法
2.5.2 Executors工具类使用可能存在的陷阱(注意)
OOM(Out Of Memory) 内存溢出
2.5.3 代码演示
public class ThreadPoolDemo3 {
public static void main(String[] args) throws Exception {
// 1、创建固定线程数据的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable()); // 已经没有多余线程了
}
}
2.5.4 总结
3. 定时器
3.1 定时器介绍
3.2 Timer定时器
public class TimerDemo1 {
public static void main(String[] args) {
// 1、创建Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程。
// 2、调用方法,处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行AAA~~~" + new Date());
}
}, 0, 2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行BB~~~"+ new Date());
//System.out.println(10/0);
}
}, 0, 2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行CCC~~~"+ new Date());
}
}, 0, 3000);
}
}
我们将第二个任务的 System.out.println(10/0); 打开,观察执行效果
可以看到,程序遇到异常就直接结束了。这就是使用Timer定时器的弊端
3.3 ScheduledExecutorService定时器
public class TimerDemo2 {
public static void main(String[] args) {
// 1、创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
// 2、开启定时任务
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出:AAA ==》 " + new Date());
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出:BBB ==》 " + new Date());
System.out.println(10 / 0);
}
}, 0, 2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出:CCC ==》 " + new Date());
}
}, 0, 2, TimeUnit.SECONDS);
}
}
我们直接带着异常条件进行测试
分析一下:线程执行完A任务后睡眠了,而线程执行完B任务后挂掉了,但是线程执行C不会受到影响。
这种方式的定时器解决了Timer定时器因任务异常挂掉而不能继续执行任务的弊端。
4. 并发,并行
4.1 什么是并发
4.2 什么是并行
4.3 总结
5. 线程的生命周期
5.1 线程的状态
5.2 线程的转换过程