补充知识
一、定时器
1、概述
- 定时器是一种控制任务延时调用,或者周期调用的技术。
- 作用:闹钟、定时邮件发送。
2、实现方式
方式一:Timer
Timer定时器
(1)构造器
构造器 | 说明 |
---|---|
public Timer() | 创建Timer定时器对象 |
(2)API
方法名称 | 说明 |
---|---|
public void schedule(TimerTask task, long delay, long period) | 开启一个定时器,按照计划处理TimerTask任务 |
package com.app.d9_timer;
import java.util.Timer;
import java.util.TimerTask;
/**
目标:学习Timer定时器的使用
*/
public class TimerDemo1 {
public static void main(String[] args) {
// 1、创建Timer定时器
Timer timer = new Timer();
// 2、开始一个定时器,按照计划处理TimerTask任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程执行一次~~");
}
}, 3000, 3000); // 程序启动3秒后处理TimerTask任务,每隔3秒会再次处理。
}
}
(3)特点和存在的问题
-
Timer是单线程
,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。
-
可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续执行任务。
package com.app.d9_timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** 目标:理解Timer定时器的特点和存在的问题 */ public class TimerDemo2 { public static void main(String[] args) { // 1、创建Timer定时器 Timer t = new Timer(); // 2、调用方法,开始一个定时器,处理任务 // A任务,程序启动后0秒后开始执行,每隔2秒执行一次 t.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程执行A任务~" + new Date()); // A任务执行一次后,线程休眠2秒钟 try { Thread.sleep(2000); }catch (Exception e) { e.printStackTrace(); } } }, 0, 2000); // B任务,程序启动后0秒后开始执行,每隔2秒执行一次 t.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程执行B任务~" + new Date()); System.out.println(10/0); // 制造一个数学问题的异常!! } }, 0, 2000); // C任务,程序启动后0秒后开始执行,每隔2秒执行一次 t.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "线程执行C任务~" + new Date()); } }, 0, 2000); } }
方式二:ScheduledExecutorService
- ScheduledExecutorService是JDK 1.5中引入了并发包,目的是为了弥补Timer的缺陷,ScheduledExecutorService内部为线程池。
(1)Executors的API
方法名称 | 说明 |
---|---|
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 获取线程池对象 |
(2)ScheduledExecutorService的API
方法名称 | 说明 |
---|---|
public ScheduledFuture< ? > scheduleAtFixedRate(Runnable command, long initalDelay, long period, TimeUnit unit) | 周期调度方法 |
package com.app.d10_scheduledExecutorService;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
目标:使用ScheduledExecutorService定时器处理定时任务
*/
public class ScheduleExecutorServiceDemo1 {
public static void main(String[] args) {
// 1、创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pools = Executors.newScheduledThreadPool(3);
// 2、开启定时任务
// A任务,程序启动后0秒后,每隔2秒执行一次
pools.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程执行A任务" + " 时间:" + new Date());
try {
Thread.sleep(100000); // 让线程进入长时间休眠~~ 模拟线程在忙
}catch (Exception e) {
e.printStackTrace();
}
}
}, 0, 2, TimeUnit.SECONDS);
// B任务,程序启动后0秒后,每隔2秒执行一次
pools.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程执行B任务" + " 时间:" + new Date());
System.out.println(10 / 0); // 制造一个bug!
}
}, 0, 2, TimeUnit.SECONDS);
// C任务,程序启动后0秒后,每隔2秒执行一次
pools.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程执行C任务" + " 时间:" + new Date());
}
}, 0, 2, TimeUnit.SECONDS);
}
}
(3)优点
- 基于线程池,某个任务的执行情况不会影响其他定时任务的执行。
二、线程并发、并行
1、概述
并发与并行
- 正在运行的程序(软件)就是一个独立的进程,线程是属于进程的,多个线程其实是并发与并行同时进行的。
(1)并发的理解
-
CPU同时处理线程的数量是有限的。
- 比如我电脑的CPU就是4核8个线程(逻辑处理器)的。
-
CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
- 不理解的话,我举两个例子:
- 1、假如咱们班的张老师是一个CPU,咱们都是线程,张老师要给咱们每个人发3颗糖,但由于老师跑得非常快,一下子就给每个人都发了3颗糖,给我们的感觉是同时发糖的,这就是并发执行。
- 2、有一个江湖剑客(CPU),由于他出剑的速度非常的快,一下子就把场上所有的强盗(线程)全部刺杀倒地了,给我们的感觉就好像是一剑全杀的,这就是并发执行。
- 不理解的话,我举两个例子:
(2)并行的理解
-
在同一时刻上,同时有多个线程在被CPU处理并执行。
- 不理解的话,我再举个例子:
- 1、假如我是孙悟空,由于我是一个人,所以每刻只能杀1个妖怪,那要是我弄出6个分身,我就可以每刻杀6个妖怪了,这就是并行。
- 不理解的话,我再举个例子:
总结
1、简单说说并发与并行的含义是?
- 并发:CPU分时轮询的执行线程。
- 并行:同一时刻同时在执行。
三、线程生命周期
1、概述
- 生命周期就是从出生到死去。
- 人一生中会经历很多状态,线程也是一样。
2、线程的状态
- 状态:就是线程从生到死的过程,以及中间经历的各种状态及状态转换。
- 理解线程的状态有利于提升并发编程的理解能力。
3、Java线程的状态
-
Java总共定义了6种状态。
线程状态 说明 NEW(新建) 线程刚被创建,但是并未启动。 RUNNABLE(可运行) 线程已经调用了start()等待CPU调度。 BLOCKED(锁阻塞) 线程在执行的时候未抢到锁对象,则该线程进入BLOCKED状态。 WAITING(无限等待) 一个线程进入WAITING状态,另一个线程调用notify()或者notifyAll()方法才能唤醒。 TIMED WAITING(计时等待) 同WAITING状态,有几个方法有超时参数,调用他们将进入TIMED WAITING状态。
带有超时参数的常用方法有Thread.sleep、Object.wait。TEMINATED(终止) 因为run()方法正常终止而死亡,或者因为没有捕获到异常终止了run()方法而死亡。 -
6种状态都定义在Thread类的内部枚举类中。
4、线程的6种状态互相转换
- 以下是线程初始化状态后可能会出现的状态:
New
:线程初始化状态后:- 调用
start()
方法后,等待CPU并发它,被并发后,进入Runnable(可运行状态)
; - 状态1:假如线程在执行小明取钱任务的时候出现了异常,执行完毕,进入
Teminated(终止死亡状态)
; - 状态2:假如线程在执行小明取钱任务的时候没有抢到锁,就会进入
Blocked(锁阻塞状态)
; - 状态3:假如线程在执行小明取钱任务的时候抢到了锁,就会从
Blocked(锁阻塞状态)
转换到Runnable(可运行状态)
; - 状态4:假如线程在执行小明取完钱后调用了
wait()
方法,就会进入Waiting(无限等待状态)
,假如小明(线程
)被小红(其他线程
)notify(唤醒)
了,但未抢到锁,就会从Waiting(无限等待状态)
转换到Blocked(锁阻塞状态)
; - 状态5:假如小明(
线程
)在Waiting(无限等待状态)
,被其他线程notify(唤醒)
了,并抢到了锁,就会从Waiting(无限等待状态)
转换到Runnable(可运行状态)
; - 状态6:假如线程在执行小明调用
(sleep(): 抱着锁休眠)
或wait()
方法让小明(线程
)进入休眠3秒钟,就会进入到Timed Waiting(计时等待状态)
; - 状态7:假如小明(
线程
)的(sleep(): 抱着锁休眠)
调用结束,说明休眠时间到,从Waiting(计时等待状态)
转换到Runnable(可运行状态)
; - 状态8:假如小明(
线程
)的wait()
调用结束,说明休眠时间到,并抢到了锁,从Waiting(计时等待状态)
转换到Runnable(可运行状态)
; - 状态9:假如小明(
线程
)的wait()
调用结束,说明休眠时间到,没有抢到锁,就会从Waiting(计时等待状态)
转换到Blocked(锁阻塞状态)
; - 状态10:假如线程在执行小明调用
wait()
方法让小明(线程
)进入休眠3秒钟,就会进入到Timed Waiting(计时等待状态)
,在休眠时间未结束前被小红(其他线程
)notify(唤醒)
了,并抢到了锁,就会从Waiting(计时等待状态)
转换到Runnable(可运行状态)
; - 状态11:假如线程在执行小明调用
wait()
方法让小明(线程
)进入休眠3秒钟,就会进入到Timed Waiting(计时等待状态)
,在休眠时间未结束前被小红(其他线程
)notify(唤醒)
了,没有抢到锁,就会从Waiting(计时等待状态)
转换到Blocked(锁阻塞状态)
; - 状态12:假如线程在执行小明取钱任务完成,进入
Teminated(终止死亡状态)
;
- 调用