线程池&死锁&线程状态&定时器
能够描述Java中线程池运行原理
使用一个容器(集合)存储一些线程
当我们要使用线程的时候,从容器中取出
使用完毕线程,把线程在归还给线程池
可以重复使用线程,可以提高程序的效率,避免了频繁的创建线程和销毁线程
能够描述死锁产生的原因
出了两个同步代码块的嵌套
线程一拿着线程二的锁
线程二拿着线程一的锁
两个线程都处于阻塞状态,都不会继续执行
能够说出线程6个状态的名称
新建,运行,阻塞,睡眠(计算等待),无限等待,死亡(退出)
能够理解等待唤醒案例
两个线程之间的协作
包子铺线程做好包子-->唤醒吃货线程-->包子铺线程等待-->吃货线程吃包子-->吃完包子-->唤醒包子铺线程-->吃货线程等待
能够使用定时器
void schedule(TimerTask task, long delay) 在指定的毫秒值之后,只执行一次定时任务
void schedule(TimerTask task, long delay, long period) 在指定的毫秒值之后,每隔多少毫秒反复执行定时任务
void schedule(TimerTask task, Date time) 在指定的时间和日期之后,只执行一次定时任务
void schedule(TimerTask task, Date firstTime, long period) 在指定的时间和日期之后,每隔多少毫秒反复执行定时任务
第一章 线程池
1.线程池的思想
2.线程池概述
/*
java.util.concurrent.Executors:是一个创建线程池的工具类,专门用来生产线程池,里边的方法都是静态的
静态方法:
static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
参数:
int nThreads:创建的线程池,包含的线程数量 100 包含100个线程 1000包含1000个线程
返回值:
ExecutorService:就是一个线程池,ExecutorService是一个接口,返回的是ExecutorService接口的实现类对象
java.util.concurrent.ExecutorService:描述线程池的接口
Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
<T> Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
参数:
Runnable task:传递Runnable接口的实现类对象(线程任务)==>重写run方法,设置线程任务==>run方法没有返回值
Callable<T> task:传递Callable接口的实现类对象(线程任务)==>重写call方法,设置线程任务==>call方法有返回值
返回值:
Future:用来接收线程任务的返回值==>用来接收call方法的返回值
*/
3.使用线程池执行Runnable接口的线程任务(重点)
package com.itheima.demo01ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
使用线程池执行Runnable接口的线程任务(重点)
使用步骤:
1.使用线程池工厂类Executors中的静态方法newFixedThreadPool,创建一个包含指定线程数量的线程池
2.创建一个Runnable接口的实现类,重写run方法,设置线程任务(匿名内部类简化)
3.使用线程池ExecutorService中的方法submit,获取线程执行线程任务
注意:submit方法会[自动]从线程池中取出一个线程,执行线程任务,执行完毕任务,会[自动]的把线程在归还给线程
*/
public class Demo01ThreadPool {
public static void main(String[] args) {
//1.使用线程池工厂类Executors中的静态方法newFixedThreadPool,创建一个包含指定线程数量的线程池
ExecutorService es = Executors.newFixedThreadPool(3);
//3.使用线程池ExecutorService中的方法submit,获取线程执行线程任务
//new Thread(new RunnableImpl()).start();
es.submit(new RunnableImpl());//pool-1-thread-1线程正在执行线程任务!
es.submit(new RunnableImpl());//pool-1-thread-3线程正在执行线程任务!
es.submit(new RunnableImpl());//pool-1-thread-2线程正在执行线程任务!
es.submit(new RunnableImpl());//pool-1-thread-3线程正在执行线程任务!
//使用匿名内部类:简化Runnable接口的实现类对象
es.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程正在执行线程任务!");
}
});
/*
void shutdown() 用于销毁线程池
相当于把鱼缸打碎了,就不能在使用了
线程池一旦被销毁,就不能在使用了
*/
//es.shutdown();
//es.submit(new RunnableImpl());//RejectedExecutionException:拒绝访问异常
}
}
package com.itheima.demo01ThreadPool;
//2.创建一个Runnable接口的实现类,重写run方法,设置线程任务(匿名内部类简化)
public class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程正在执行线程任务!");
}
}
4.使用线程池执行Callable接口的线程任务(重点)
package com.itheima.demo01ThreadPool;
import java.util.Random;
import java.util.Scanner;
import java.util.concurrent.*;
/*
使用线程池执行Callable接口的线程任务(重点)
java.util.concurrent.Callable<V>接口
Callable 接口类似于 Runnable,两者都用用于创建一个线程任务
但是 Runnable 不会返回结果,Callable有返回结果
Callable接口中的方法
V call() 计算结果,如果无法计算结果,则抛出一个异常。
call方法是一个有返回值的方法,返回值的类型使用的是接口上的泛型
接口使用什么泛型,call方法就返回一个什么类型的值
Callable<Integer>==>call方法就返回一个Integer类型的数据
Callable<String>==>call方法就返回一个String类型的数据
-------------------------------------------------------------------------
<T> Future<T> submit(Callable<T> task)
提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
当我们使用submit方法执行Callable接口的线程任务,会把call的返回值存储到Future对象中
我们就需要从Future对象中,在取出call方法的返回值来使用
-------------------------------------------------------------------------
使用步骤(重点):
1.使用线程池工厂类Executors中的静态方法newFixedThreadPool,创建一个包含指定线程数量的线程池
2.使用线程池ExecutorService中的方法submit,执行Callable接口的线程任务
3.从submit方法的返回值Future对象中取出call方法的返回值
*/
public class Demo02ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.使用线程池工厂类Executors中的静态方法newFixedThreadPool,创建一个包含指定线程数量的线程池
ExecutorService es = Executors.newFixedThreadPool(3);
//2.使用线程池ExecutorService中的方法submit,执行Callable接口的线程任务
Future<Integer> f1 = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//返回一个0-9之间的随机整数(线程任务)
//Random r = new Random();
//int n = r.nextInt(10);
//return n;
return new Random().nextInt(10);
}
});
//System.out.println(f1);//java.util.concurrent.FutureTask@14ae5a5
//3.从submit方法的返回值Future对象中取出call方法的返回值
/*
java.util.concurrent.Future<V>接口
V get() 如有必要,等待计算完成,然后获取其结果。
取出Future对象中存储的call方法的返回值
*/
System.out.println(f1.get());
System.out.println("------------------------------");
Future<String> f2 = es.submit(new Callable<String>() {
@Override
public String call() throws Exception {
//返回一个键盘输入的字符串(线程任务)
System.out.println("请输入一个字符串:");
return new Scanner(System.in).nextLine();
}
});
System.out.println(f2.get());
}
}
5.线程池的练习
需求: 使用线程池方式执行任务,返回1-n的和
package com.itheima.demo01ThreadPool;
import java.util.Scanner;
import java.util.concurrent.*;
/*
线程池的练习
需求: 使用线程池方式执行任务,返回1-n的和
分析:
线程任务返回1-n的和,有返回值,使用Callable接口完成
实现步骤:
1.使用Scanner获取一个用户键盘输入的整数n
2..创建一个包含指定线程数量的线程池
3.使用线程池中的方法submit执行,返回1-n的和的任务
*/
public class Demo03Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1.使用Scanner获取一个用户键盘输入的整数n
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数:");
int n = sc.nextInt();
//2..创建一个包含指定线程数量的线程池
ExecutorService es = Executors.newFixedThreadPool(3);
//3.使用线程池中的方法submit执行,返回1-n的和的任务
Future<Integer> f = es.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//计算1-n的和(线程任务)
int sum = 0;
for (int i = 1; i <= n; i++) {
sum+=i;
}
return sum;
}
});
System.out.println("1到"+n+"的和为:"+f.get());
}
}
第二章 死锁
1.死锁的原理
2.死锁的代码实现(重点)
package com.itheima.demo02DeadLock;
public class Demo01DeadLock {
public static void main(String[] args) {
//创建两个线程,去执行线程任务
RunnableImpl run = new RunnableImpl();
new Thread(run).start();
new Thread(run).start();
}
}
package com.itheima.demo02DeadLock;
/*
死锁:
两个线程,你拿着我的锁,我拿着你的锁,导致都无法继续执行
前提:
1.必须有两个以上的线程
2.必须有两个以上的锁对象
3.必须出现同步代码块的嵌套
*/
public class RunnableImpl implements Runnable{
//创建两个锁对象,可以是任意的对象,保证多个线程使用的都是这两个对象
private String lockA = "A锁";
private String lockB = "B锁";
@Override
public void run() {
//定义一个死循环,让线程任务重复执行
while (true){
//同步代码块的嵌套
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"...in to lockA...");
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"...in to lockB...");
}
}
//同步代码块的嵌套
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"...in to lockB...");
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"...in to lockA...");
}
}
}
}
}
执行结果
Thread-0...in to lockA...
Thread-0...in to lockB...
Thread-0...in to lockB...
Thread-0...in to lockA...
Thread-0...in to lockA...
Thread-0...in to lockB...
Thread-0...in to lockB...
Thread-1...in to lockA...
注意:我们平时写代码应该尽量避免死锁
第三章 线程状态
1.线程状态概述
重点:必须记住6种线程状态的名称
新建(NEW):刚刚创建出来但是没有运行的线程处于此状态。
运行(RUNNABLE):调用start方法启动后的线程处于运行状态。
受阻塞(BLOCKED):等待获取锁的线程处于此状态。
无限等待(WAITING):当线程调用wait()方法时,线程会处于无限等待状态【没有时间的等待】
计时等待(睡眠)(TIMED_WAITING):当线程调用wait(毫秒值)方法或sleep(毫秒值)时,线程会处于计时等待状态【有时间的等待】
退出(死亡)(TERMINATED):当线程执行完了自己的run方法或者调用了stop方法,会进入退出状态。
2.Object类中等待与唤醒的方法
java.lang.Object 类:里边的方法,任意的一个类都可以使用
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify()
醒在此对象监视器(同步锁,对象锁)上等待的单个线程。
void notifyAll()
唤醒在此对象监视器(同步锁,对象锁)上等待的所有线程。
注意:
1.wait方法和notify方法一般都是使用在同步代码块中==>有锁对象==>对象监视器
2.一般都是使用锁对象调用wait和notify方法(保证多个线程使用的是同一个锁对象)
Thread-0线程使用锁对象-->wait方法-->Thread-0线程进入无限等待状态
Thread-1线程使用锁对象-->notify方法-->唤醒在锁对象上等待的Thread-0线程
3.在同步中的线程调用wait方法,进入到等待,会释放锁对象的
在同步中线程调用sleep方法,进入睡眠,不会释放锁对象的
package com.itheima.demo03waitAndnotify;
/*
练习使用Object类中的wait和notify方法
需求:
线程一:调用wait方法,进入到无限等待状态,就不在继续执行了
让程序睡眠3秒钟
线程二:调用notify方法,唤醒等待的线程一
*/
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
//定义一个锁对象,供两个线程使用
String lock = "锁";
//线程一:调用wait方法,进入到无限等待状态,就不在继续执行了
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程一开始执行线程任务了!");
//同步代码块
synchronized (lock){
System.out.println("线程一:调用wait方法,进入到无限等待状态,就不在继续执行了");
//使用锁对象调用wait方法
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程一被线程二唤醒了,继续执行线程任务!");
}
}).start();
//让程序睡眠3秒钟(3秒钟之后,在唤醒线程一)
Thread.sleep(3000);
//线程二:调用notify方法,唤醒等待的线程一
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程二开始执行线程任务了!");
//同步代码块
synchronized (lock){
System.out.println("线程二:调用notify方法,唤醒等待的线程一");
//使用锁对象调用notify方法
lock.notify();
}
System.out.println("线程二唤醒线程一之后,结束了线程任务!");
}
}).start();
}
}
3.等待与唤醒案例(重点)
a.需求分析
b.代码实现
package com.itheima.demo04waitAndnotify;
/*
分析:案例中使用的类
1.包子类:资源类
属性:皮,馅,状态(有|没有) false
*/
public class BaoZi {
//皮
String pi;
//馅
String xian;
//状态(有|没有) false
boolean flag = false;
}
package com.itheima.demo04waitAndnotify;
/*
2.包子铺类:是一个线程类(继承Thread,实现Runnable)
线程任务:做包子
对包子的状态进行判断
true:有包子
包子铺线程等待 包子对象:wait();
false:没有包子
包子铺线程做包子
打印做x皮x馅的包子
花3秒钟做包子
3秒钟之后做好包子
修改包子的状态:有
唤醒吃货线程吃包子
注意:
必须保证包子铺线程和吃货线程只能有一个在运行(要么做包子,要么在吃包子)
可以使用同步代码块==>只让一个线程执行==>需要一个锁对象==>保证两个线程使用的是同一个锁对象
可以使用包子对象作为锁对象(唯一)
定义一个包子类型的变量,使用构造方法为变量赋值
*/
//包子铺类:是一个线程类(继承Thread,实现Runnable)
public class BaoZiPu implements Runnable{
//定义一个包子类型的变量,使用构造方法为变量赋值
private BaoZi bz;
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
//线程任务:做包子
@Override
public void run() {
//增加一个死循环,让包子铺线程一直做包子
while (true){
//必须保证包子铺线程和吃货线程只能有一个在运行==>同步代码块
synchronized (bz){
//对包子的状态进行判断
if(bz.flag==true){
//true:有包子 包子铺线程等待 包子对象:wait();
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//false:没有包子 包子铺线程做包子==>被吃货线程唤醒之后执行
//打印做x皮x馅的包子
bz.pi = "薄皮";
bz.xian = "牛肉大葱馅";
System.out.println("包子铺线程正在做:"+bz.pi+bz.xian+"的包子!");
//花3秒钟做包子
System.out.println("包子铺线程做包子需要3秒钟时间!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//3秒钟之后做好包子,修改包子的状态:有
bz.flag = true;
//唤醒吃货线程吃包子
bz.notify();//唤醒的是在包子对象上等待的吃货线程
System.out.println("包子铺线程已经做好了:"+bz.pi+bz.xian+"的包子,吃货线程赶紧来吃包子吧!");
}
}
}
}
package com.itheima.demo04waitAndnotify;
/*
3.吃货类:是一个线程类
线程任务:吃包子
对包子的状态进行判断
false:没有包子
吃货线程等待 包子对象:wait();
true:有包子
吃货线程吃包子
打印吃x皮x馅的包子
花1秒钟吃包子
1秒钟之后吃完包子
修改包子的状态为:没有
唤醒包子铺线程做包子
注意:
必须保证包子铺线程和吃货线程只能有一个在运行(要么做包子,要么在吃包子)
可以使用同步代码块==>只让一个线程执行==>需要一个锁对象==>保证两个线程使用的是同一个锁对象
可以使用包子对象作为锁对象(唯一)
定义一个包子类型的变量,使用构造方法为变量赋值
*/
//3.吃货类:是一个线程类
public class ChiHuo implements Runnable{
//定义一个包子类型的变量,使用构造方法为变量赋值
private BaoZi bz;
public ChiHuo(BaoZi bz) {
this.bz = bz;
}
//线程任务:吃包子
@Override
public void run() {
//增加一个死循环,让吃货线程一直吃包子
while (true){
//必须保证包子铺线程和吃货线程只能有一个在运行==>同步代码块
synchronized (bz){
//对包子的状态进行判断
if(bz.flag==false){
//false:没有包子 吃货线程等待 包子对象:wait();
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//true:有包子 吃货线程吃包子 被包子铺线程唤醒之后执行
//打印吃x皮x馅的包子
System.out.println("吃货线程正在吃:"+bz.pi+bz.xian+"的包子!");
//花1秒钟吃包子
System.out.println("吃货线程吃包子需要花1秒钟时间");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//1秒钟之后吃完包子 修改包子的状态为:没有
bz.flag=false;
//唤醒包子铺线程做包子
bz.notify();//唤醒的是包子对象上等待的包子铺线程
System.out.println("吃货线程已经吃完:"+bz.pi+bz.xian+"的包子,包子铺线程赶紧做包子吧!");
System.out.println("-------------------------------------------------------------------");
}
}
}
}
package com.itheima.demo04waitAndnotify;
/*
4.测试类(包含main方法类)
创建包子对象,
创建2个线程,一个包子铺线程做包子,一个吃货线程吃包子
*/
public class Demo01 {
public static void main(String[] args) {
//创建包子对象
BaoZi bz = new BaoZi();
//创建2个线程,一个包子铺线程做包子,一个吃货线程吃包子
new Thread(new BaoZiPu(bz)).start();
new Thread(new ChiHuo(bz)).start();
}
}
执行的流程:
包子铺线程正在做:薄皮牛肉大葱馅的包子!
包子铺线程做包子需要3秒钟时间!
包子铺线程已经做好了:薄皮牛肉大葱馅的包子,吃货线程赶紧来吃包子吧!
吃货线程正在吃:薄皮牛肉大葱馅的包子!
吃货线程吃包子需要花1秒钟时间
吃货线程已经吃完:薄皮牛肉大葱馅的包子,包子铺线程赶紧做包子吧!
-------------------------------------------------------------------
包子铺线程正在做:薄皮牛肉大葱馅的包子!
包子铺线程做包子需要3秒钟时间!
包子铺线程已经做好了:薄皮牛肉大葱馅的包子,吃货线程赶紧来吃包子吧!
吃货线程正在吃:薄皮牛肉大葱馅的包子!
吃货线程吃包子需要花1秒钟时间
吃货线程已经吃完:薄皮牛肉大葱馅的包子,包子铺线程赶紧做包子吧!
-------------------------------------------------------------------
包子铺线程正在做:薄皮牛肉大葱馅的包子!
包子铺线程做包子需要3秒钟时间!
包子铺线程已经做好了:薄皮牛肉大葱馅的包子,吃货线程赶紧来吃包子吧!
吃货线程正在吃:薄皮牛肉大葱馅的包子!
吃货线程吃包子需要花1秒钟时间
吃货线程已经吃完:薄皮牛肉大葱馅的包子,包子铺线程赶紧做包子吧!
-------------------------------------------------------------------
包子铺线程正在做:薄皮牛肉大葱馅的包子!
包子铺线程做包子需要3秒钟时间!
包子铺线程已经做好了:薄皮牛肉大葱馅的包子,吃货线程赶紧来吃包子吧!
吃货线程正在吃:薄皮牛肉大葱馅的包子!
吃货线程吃包子需要花1秒钟时间
吃货线程已经吃完:薄皮牛肉大葱馅的包子,包子铺线程赶紧做包子吧!
-------------------------------------------------------------------
...
第四章 定时器
1.定时器的概述
定时器,可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情
/*
java.util.Timer类:描述定时器的类
一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
构造方法:
Timer() 创建一个新计时器。
成员方法:
void cancel() 终止此计时器,丢弃所有当前已安排的任务。
注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。
void schedule(TimerTask task, long delay) 在指定毫秒值之后,执行指定的任务,只会执行一次
参数:
task - 所要安排的任务。定时器执行的任务
delay - 执行任务前的延迟时间,单位是毫秒。多少毫秒之后开始执行TimerTask任务
void schedule(TimerTask task, long delay, long period) 在指定毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复的执行定时任务
参数:
task - 所要安排的任务。定时器执行的任务
delay - 执行任务前的延迟时间,单位是毫秒。多少毫秒之后开始执行TimerTask任务
period - 执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务,只会执行一次
参数:
task - 所要安排的任务。定时器执行的任务
time - 执行任务的时间。从什么日期(Date对象)开始执行任务 2020-05-12 14:45:30
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
参数:
task - 所要安排的任务。定时器执行的任务
firstTime - 首次执行任务的时间。从什么日期(Date对象)开始执行任务 2020-05-12 14:45:30
period - 执行各后续任务之间的时间间隔,单位是毫秒。 定时器开始执行之后,每隔多少毫秒重复执行
java.util.TimerTask:类 implements Runnable接口
由 Timer 安排为一次执行或重复执行的任务。
void run() 此计时器任务要执行的操作。重写run方法,设置线程任务
*/
2.定时器的使用(重点)
package com.itheima.demo05Timer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/*
定时器的使用
*/
public class Demo01Timer {
public static void main(String[] args) throws ParseException {
show04();
}
/*
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
参数:
task - 所要安排的任务。定时器执行的任务
firstTime - 首次执行任务的时间。从什么日期(Date对象)开始执行任务 2020-05-12 14:45:30
period - 执行各后续任务之间的时间间隔,单位是毫秒。 定时器开始执行之后,每隔多少毫秒重复执行
需求:
在2021-12-4 16:02:00,第一次执行定时任务,之后每间隔3秒钟执行一次
*/
private static void show04() throws ParseException {
//创建一个定时器
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2021-12-4 16:02:00");
//创建一个定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("哈哈");
}
},date,3000);
}
/*
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务,只会执行一次
参数:
task - 所要安排的任务。定时器执行的任务
time - 执行任务的时间。从什么日期(Date对象)开始执行任务
需求:
在2021-12-4 15:58:30,执行定时任务,只执行一次
注意:
如果设置的时间已经过了,那么会直接执行定时任务
*/
private static void show03() throws ParseException {
//创建一个定时器
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2021-12-4 15:58:30");
//创建一个定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("哈哈");
//执行完一次定时任务,销毁定时器
timer.cancel();
}
},date);
}
/*
void schedule(TimerTask task, long delay, long period) 在指定毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复的执行定时任务
参数:
task - 所要安排的任务。定时器执行的任务
delay - 执行任务前的延迟时间,单位是毫秒。多少毫秒之后开始执行TimerTask任务
period - 执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
需求:
在10秒钟之后,第一次执行定时任务,之后每间隔1秒钟执行一次定时任务
*/
private static void show02() {
//创建一个定时器
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//创建一个定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(sdf.format(new Date()));
}
},10000,1000);
}
/*
void schedule(TimerTask task, long delay) 在指定毫秒值之后,执行指定的任务,只会执行一次
参数:
task - 所要安排的任务。定时器执行的任务
delay - 执行任务前的延迟时间,单位是毫秒。多少毫秒之后开始执行TimerTask任务
需求:
在5秒钟之后,执行定时任务,只执行一次
*/
private static void show01() {
//创建一个定时器对象
Timer timer = new Timer();
//设置一个定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("5秒钟之后,c4爆炸了! 嘭...");
//cancel()终止此计时器
timer.cancel();
}
},5000);
}
}