多线程的概述
进程:
正在运行的程序,是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
线程:
进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则成为单线程程序
一个进程如果有多条执行路径,则成为多线程程序。
一个进程内可以执行多个任务,则每个任务是一个线程。
多进程有什么意义?
单进程计算机只能做一件事,现在的计算机同一时间段内可以执行多个任务,提高CPU的使用率。
多线程有什么意义?
多线程的存在,不是提高程序的执行速度,其实是为了提高应用程序的使用率。
程序执行其实就是抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到。
无法保证线程在哪个时刻抢到资源,线程执行有随机性。
并发:逻辑上同时发生,指在某个时间内同时运行多个程序。
并行:物理上同时发生,指在某个时间点运行多个程序。、
Java程序运行原理
java命令启动java虚拟机,启动JVM,等于启动一个应用程序,也就是启动一个进程。
该进程会自动启动一个主线程,然后主线程去调用某个类的main方法,所以main方法运行在主线程中,
再次之前程序都是单线程的。
JVM是多线程,垃圾回收线程也要先东西,否则会很容易出现内存溢出。
最少启动了主线程和垃圾回收线程两个线程。
由于线程是依赖进程存在的,所以我们应该先调用一个进程。进程是由系统创建的,所以我们应该调用系统功能创建一个进程。
Java是不能调用系统功能的,所以我们没办法直接实现多线程程序。Java可以调用C/C++写好的程序实现多线程,由C/C++调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用,我们就可以实现多线程程序了。
Java提供的类:Thread(线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。)
两种方式实现多线程程序:
1、一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例
//继承Thread类实现多线程 public class ThreadTest extends Thread{ public void run(){ for(int i = 0; i < 20; i++){ System.out.println(new Thread().getName() + "---" + i); } } public static void main(String[] args) { ThreadTest t = new ThreadTest(); t.start(); } }
2、声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread
时作为一个参数来传递并启动
//实现Runnable接口实现多线程 public class RunableDemo implements Runnable{ public static void main(String[] args) { //创建实现接口类对象 RunableDemo rd = new RunableDemo(); //把对象传入Thread类中 Thread t = new Thread(rd); t.start(); } public void run() { for(int i = 0; i < 10; i++){
//Runnable接口只能通过Thread.currentThread().getName()获取名称
//currentThread()方法返回的是一个Thread类 System.out.println(Thread.currentThread().getName() + "---" + i); } } }
public class ThreadTest extends Thread{ public void run(){ for(int i = 0; i < 20; i++){ //继承Thread类不需要使用new Thread().getName(),直接调用getName()方法就可以 System.out.println(getName() + "---" + i); } } public static void main(String[] args) { ThreadTest t = new ThreadTest(); t.setName("线程"); t.start(); } }
设置线程名
public class ThreadTest extends Thread{ public ThreadTest(){} //调用父类的构造方法,才能在new对象的时候直接设置线程名 public ThreadTest(String name) { super(name); } public void run(){ for(int i = 0; i < 20; i++){ System.out.println(getName() + "---" + i); } } } //----------------------------------------------------------------- public class Demo { public static void main(String[] args) { //类中必须实现父类的构造方法,才能在创建对象的时候给线程设置名称 ThreadTest tt = new ThreadTest("线程"); tt.start(); } }
但是上面方式无法获取main方法所在线程的名称,这种时候调用Thread的currentThread方法,返回一个Thread类。
//static Thread currentThread() 返回对当前正在执行的线程对象的引用。 public void run(){ for(int i = 0; i < 20; i++){ System.out.println(Thread.currentThread().getName() + "---" + i); //System.out.println(getName() + "---" + i); }
线程有两种调度模型:
1、分时调度模型,所有线程轮流使用CPU的使用权,平均每个线程占用CPU的时间片
2、抢占式调度模型,优先让优先级高的线程使用CPU,如果线程优先级相同,随机选一个。优先级高的线程抢到的时间片多一些。
Java使用的是抢占式的调度模型,设置获取线程的优先级:
public class ThreadTest extends Thread{ public ThreadTest() { } public ThreadTest(String name) { super(name); } public void run() { for (int i = 0; i < 20; i++) { // 获取对象优先级,默认优先级是5. System.out.println(Thread.currentThread().getName() + "---" + "getPriority()" + "---" + getPriority()); } } } //---------------------------------------------------------------------- ThreadTest tt = new ThreadTest("线程"); ThreadTest tt2 = new ThreadTest("线程2"); //void setPriority(int newPriority) 更改线程的优先级。 //优先级的范围:1 —— 10之间 tt.setPriority(10); tt2.setPriority(1); tt.start(); tt2.start();
线程睡眠
import java.util.Date; public class ThreadSleep extends Thread{ public ThreadSleep() {} public ThreadSleep(String name){ super(name); } @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + i + "___日期:" + new Date() ); //sleep /*static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 static void sleep(long millis, int nanos) 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 */ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //---------------------------------------------------------------------- public class Demo { public static void main(String[] args) { ThreadSleep ts1 = new ThreadSleep(); ThreadSleep ts2 = new ThreadSleep(); ThreadSleep ts3 = new ThreadSleep(); ts1.setName("线程1"); ts2.setName("线程2"); ts3.setName("线程3"); ts1.start(); ts2.start(); ts3.start(); } }
线程加入
public class ThreadJoin extends Thread { public ThreadJoin() { } public ThreadJoin(String name) { super(name); } public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "---" + i); } } } //------------------------------------------------------------------------- public class Demo { public static void main(String[] args) throws InterruptedException { ThreadJoin ts1 = new ThreadJoin(); ThreadJoin ts2 = new ThreadJoin(); ThreadJoin ts3 = new ThreadJoin(); ts1.setName("线程1"); ts2.setName("线程2"); ts3.setName("线程3"); ts1.start(); /* void join(long millis) 等待该线程终止的时间最长为 millis 毫秒。 void join(long millis, int nanos) 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。 */ //必须放在start()方法之后,表示这个线程走完了,其他线程才能执行 ts1.join(); ts2.start(); ts3.start(); } }
线程礼让
public class ThreadYield extends Thread { public void run() { for (int i = 0; i < 20; i++) { System.out.println(getName() + "---" + i); /* * static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 */ // 让多个线程的执行更和谐,但不能靠它保证一个线程一次 Thread.yield(); } } } //------------------------------------------------------------------------ public class Demo { public static void main(String[] args) throws InterruptedException { ThreadYield ty1 = new ThreadYield(); ThreadYield ty2 = new ThreadYield(); ty1.setName("线程1"); ty2.setName("线程2"); ty1.start(); ty2.start(); } }
后台线程
public class ThreadDaemon extends Thread { public void run() { for (int i = 0; i < 20; i++) { System.out.println(getName() + "---" + i); } } } //--------------------------------------------------------------- /*setDaemon public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 */ public class Demo { public static void main(String[] args) throws InterruptedException { /* 后台线程 public final void setDaemon(boolean on);*/ ThreadDaemon td1 = new ThreadDaemon(); ThreadDaemon td2 = new ThreadDaemon(); td1.setName("线程1"); td2.setName("线程2"); //设置守护线程 td1.setDaemon(true); td2.setDaemon(true); td1.start(); td2.start(); //主线程死掉,守护线程也会死掉 Thread.currentThread().setName("main线程"); for(int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName() + i); } } }
线程终止
import java.util.Date; public class ThreadStop extends Thread { public void run() { for (int i = 0; i < 20; i++) { System.out.println("开始执行:" + "---" + new Date()); try { Thread.sleep(3000); } catch (InterruptedException e) { System.out.println("线程被终止"); } System.out.println("线程终止时间:" + new Date()); } } } //-------------------------------------------------------------------- /*中断线程 public final void stop();已过时。 该方法具有固有的不安全性 public void interrupt();中断线程,把线程的状态终止 static boolean interrupted() 测试当前线程是否已经中断。 */ public class Demo { public static void main(String[] args) { ThreadStop ts1 = new ThreadStop(); ts1.start(); try { Thread.sleep(1000); ts1.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程生命周期
新建:创建线程对象
就绪:线程有执行的资格,没有执行权
运行:有执行资格,有执行权
阻塞:由于一些操作,让线程处于该状态,没有执行资格,没有执行权而另一些操作可以把它给激活,激活后处于就绪状态
死亡:线程对象编程垃圾,等待被回收
实现Runnable买票的问题
public class RunableDemo implements Runnable{ private int i = 100; public void run() { while(i > 0){ System.out.println(Thread.currentThread().getName() + "---" + (i--)); //i--; } } } //------------------------------------------------------------------------ public class Demo { public static void main(String[] args) { RunableDemo rd = new RunableDemo(); Thread t1 = new Thread(rd); Thread t2 = new Thread(rd); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } }
延迟就出错了
public class RunableDemo implements Runnable{ private int i = 100; public void run() { while(i > 0){ System.out.println(Thread.currentThread().getName() + "---" + (i--)); //延迟程序就出错了,相同的数字出现多次(随机性和延迟导致的),输出: /* 线程1---100 线程2---99 线程1---98 线程2---98 线程1---97 线程2---97 线程2---95 线程1---96 线程2---94 线程1---93*/ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
解决线程安全问题原因:
1、是否是多线程环境
2、是否有共享数据
3、是否有多条语句操作共享数据
1、2我们改不了,就改变3,把多条语句操作共享数据的代码宝成一个整体,让别人无法执行。
Java提供了同步机制。
同步代码块格式
synchronized(对象){需要同步的代码} //同步可以解决安全问题的根本原因在那个对象,该对象如同锁的功能
public class RunableDemo implements Runnable { private int i = 100; //创建锁对象 private Object obj = new Object(); public void run() { while (true) { // 多个线程必须是同一把锁
// synchronized (obj) { if (i > 0) { System.out.println(Thread.currentThread().getName() + "---" + (i--)); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
改进版:
public class RunableDemo implements Runnable { private int i = 100; private Object obj = new Object(); private int j = 0; public void run() { while (true) { if (j % 2 == 0) { synchronized (obj) { if (i > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售" + "---" + (i--)); } } } else { synchronized (obj) { if (i > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售" + "---" + (i--)); } else { } } } } } }
同步的特点:
前提条件:
多个线程
解决问题的时候多个线程必须使用同一把锁
解决了线程安全问题
弊端
当线程很多时,每个线程都会去判断同步上的锁,耗费资源,降低程序效率。
如果一个方法一进去就看到了代码被同步,那么可以把同步加在方法上,同步方法的格式:
//把同步关键字加在方法上 private synchronized void sellTicket(){ //方法体 }
同步代码块锁对象:任意对象 同步方法的锁对象:当前类(this) 静态方法及所对象问题: 静态是随着类的加载而加载 静态方法同步锁对象是所在类的.class文件。
List<String> list = new ArrayList<String>(); 线程不安全 List<String> list = Collection.synchronizedList(new ArrayList<String>()); 线程安全
synchronized
Lock锁
//虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,JDk5以后提供了新的锁对象Lock。 Lock void lock(); void unlock(); ReentrantLock
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo implements Runnable { private int i = 100; private Lock lock = new ReentrantLock(); public void run() { while (true) { //使用try...finally语句,在finally语句中释放锁,最终程序都会执行到finally,释放锁就不会失败。 try { //获取锁 lock.lock(); if(i > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "出售: " + (i--) ); } } finally { //释放锁 lock.unlock(); } } } }
ReentrantLock是Lock的实现类。
同步弊端
效率低
如何出现了同步嵌套,就容易产生死锁。
死锁问题及其代码
是指两个或两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。
同步代码块的嵌套案例。
public class DieLock implements Runnable{ private final Object objA = new Object(); private final Object objB = new Object(); boolean b = false; public DieLock(boolean b){ this.b = b; } //objA和objB同时等待对方释放锁,就会出现死锁情况。 public void run() { if(b){ synchronized(objA){ System.out.println("if objA"); synchronized(objB){ System.out.println("if objB"); } } }else{ synchronized(objB){ System.out.println("else objB"); synchronized(objA){ System.out.println("else objA"); } } } } }
设置获取资源:不同种类的线程针对同一资源的操作
public class Student { String name; int age; } //-------------------------------------- public class GetStudent implements Runnable{ private Student s; public GetStudent(Student s){ this.s = s; } public void run() { //获取资源并输出 System.out.println(s.name + "---" + s.age); } } //-------------------------------------- public class SetStudent implements Runnable{ private Student s; public SetStudent(){} public SetStudent(Student s){ this.s = s; } //设置资源 public void run() { s.name = "zed"; s.age = 18; } } //-------------------------------------- public class Demo { public static void main(String[] args) { //创建共有资源 Student s = new Student(); //设置和获取类 GetStudent gs = new GetStudent(s); SetStudent ss = new SetStudent(s); //线程类 Thread t1 = new Thread(gs); Thread t2 = new Thread(ss); //线程启动 t1.start(); t2.start(); } }
上面程序t1和t2抢资源,但是如果获取线程先抢到,就无法输出资源了,因为设置线程没有设置。
所以上面的程序是有问题的。不同种类的线程都要加锁,并且不同种类加的锁必须是同一把。改进版:
public class Student { String name; int age; } //-------------------------------------- public class GetStudent implements Runnable{ private Student s; public GetStudent(Student s){ this.s = s; } public void run() { //获取资源并输出 while(true){ //不同种类的线程要用同一把锁 synchronized(s){ System.out.println(s.name + "---" + s.age); } } } } //-------------------------------------- public class SetStudent implements Runnable{ private Student s; public SetStudent(){} public SetStudent(Student s){ this.s = s; } private int i = 0; //设置资源 public void run() { while(true){ //不同种类的线程要用同一把锁 synchronized(s){ if(i % 2 ==0){ s.name = "zed"; s.age = 20; }else{ s.name = "akl"; s.age = 18; } } i++; } } } //-------------------------------------- public class Demo { public static void main(String[] args) { //创建共有资源 Student s = new Student(); //设置和获取类 GetStudent gs = new GetStudent(s); SetStudent ss = new SetStudent(s); //线程类 Thread t1 = new Thread(gs); Thread t2 = new Thread(ss); //线程启动 t1.start(); t2.start(); } }
线程安全解决了,但是存在如下问题:
如果消费者先抢到CPU的执行权,就会去消费数据,但是现在的数据是默认值,没有意义,应该等着数据有意义再消费。
如果生产者先抢到CPU的执行权,就会去产生数据,但是它产生完数据后,还继续拥有执行权,它又继续产生数据。应该等着消费者把数据消费掉,然后再生产。
正确的思路
生产者
先看是否有数据,有就等待,没有就生产。生产完之后通知消费者来消费。
消费者
先看是否有数据,有就消费,没有就等待。没有通知生产者生产。
为了处理这样的问题,Java提供了一种机制:等待和唤醒机制。
等待唤醒(Object中提供了三个方法):
void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
void notify() 唤醒在此对象监视器上等待的单个线程。
void notifyAll() 唤醒在此对象监视器上等待的所有线程。
为什么这些方法不定义在Thread中呢?
这些方法的调用必须通过锁对象调用,而我们使用的锁对象是任意锁对象。所以必须定义在Object中。
public class Student { String name; int age; boolean flag;//默认情况下没有数据,如果是true,说明有数据。 } //------------------------------------------------------------------- public class GetStudent implements Runnable{ private Student s; public GetStudent(Student s){ this.s = s; } public void run() { //获取资源并输出 while(true){ //不同种类的线程要用同一把锁 synchronized(s){
//如果flag为false,则表示没有数据,则等待。 if(!s.flag ){ try { s.wait();//将来醒来是从这里开始的 } catch (InterruptedException e) { e.printStackTrace(); } }
//flag为true,有数据输出,然后赋值flag为false,唤醒线程,大家一起来抢CPU System.out.println(s.name + "---" + s.age); //设置标记,表示没有数据。放开线程,大家一起来抢CPU。(输出了就没数据) s.flag= false; s.notify(); } } } } //------------------------------------------------------------------- public class SetStudent implements Runnable{ private Student s; public SetStudent(){} public SetStudent(Student s){ this.s = s; } private int i = 0; public void run() { while(true){ synchronized(s){
//如果flag为true,也就是说有数据,则等待 if(s.flag){ try { s.wait();//将来醒来是从这里开始的 } catch (InterruptedException e) { e.printStackTrace(); } } if(i % 2 ==0){ s.name = "zed"; s.age = 20; }else{ s.name = "akl"; s.age = 18; } i++; //修改标记,表示有数据(创建了就有数据) s.flag = true; //唤醒线程 s.notify(); } } } } //------------------------------------------------------------------- public class Demo { public static void main(String[] args) { //创建共有资源 Student s = new Student(); //设置和获取类 GetStudent gs = new GetStudent(s); SetStudent ss = new SetStudent(s); //线程类 Thread t1 = new Thread(gs); Thread t2 = new Thread(ss); //线程启动 t1.start(); t2.start(); } }
flag为true就表示有数据,为flase表示没有数据。默认值为false,表示没有数据。先生产再消费。
set public void run(){ while(true){ synchronized(s){ //为true表示有数据 if(s.flag){ //有数据就等待 s.wait(); } s.name = s.age = //设置了表示有数据了,唤醒线程抢CPU。 s.flag = true; s.notify(); } } } get: public void run(){ while(true){ synchronized(s){ //为false表示没有数据 if(!s.flag){ //没有数据就等待 s.wait(); } System.out.println("有数据,输出数据"); //输出完就没有数据了,唤醒线程抢CPU。 s.flag = false; s.notify(); } } }
也可以在学生类中做同步
public synchronized void set(String name, int age){ if(this.flag){ try{ this.wait(); }catch(InterruptedException e){ } this.name = name; this.age = age; //修改标记 this.flag = true; this.notify(); } } public synchronized void get(){ //没有数据就等待 if(!this.flag){ this.wait(); } System.out.println("输出"); //修改标记 this.flag = false; this.notify(); }
线程组
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有线程都属于主线程组(main线程组)
public final ThreadGroup getThreadGroup()
我们也可以给线程设置分组
Thread(ThreadGroup group, Runnable target, String name)
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存周期短的线程时,更应该考虑线程池,
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池成为空闲状态,等待下一个对象来使用,
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池。
static ExecutorService newCachedThreadPool() //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 static ExecutorService newFixedThreadPool(int nThreads) //创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 static ExecutorService newSingleThreadExecutor() //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行 //这些方法返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程,它提供了如下方法: <T> Future<T> submit(Callable<T> task) //提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。 Future<?> submit(Runnable task) //提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
public class ExecutorRunnable implements Runnable { @Override public void run() { for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + "----" + i); } } } //------------------------------------------------------------------ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args) { ExecutorService es = Executors.newFixedThreadPool(2); //使用下面的方法线程就会执行,但是不会关闭。 //直到调用shutdown()方法 es.submit(new ExecutorRunnable()); es.submit(new ExecutorRunnable()); es.shutdown(); } }
Callable
import java.util.concurrent.Callable; //Callable是带泛型的接口,接口泛型内的类型就是call方法返回值的类型;如果没有指定泛型类型,默认Object类。 //如果线程执行完要返回一个接口,就用Callable public class CallableDemo implements Callable<Object> { @Override public Object call() throws Exception { // TODO 自动生成的方法存根 for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + "----" + i); } return null; } } //------------------------------------------------------------------ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo { public static void main(String[] args) { ExecutorService es = Executors.newFixedThreadPool(2); //使用下面的方法线程就会执行,但是不会关闭。 //直到调用shutdown()方法 es.submit(new CallableDemo()); es.submit(new CallableDemo()); es.shutdown(); } }
import java.util.concurrent.Callable; /* * 线程求和案例 */ public class MyCallable implements Callable<Integer> { private int number; public MyCallable(int number) { this.number = number; } @Override public Integer call() throws Exception { int sum = 0; for (int x = 1; x <= number; x++) { sum += x; } return sum; } } //------------------------------------------------------- public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { // 创建线程池对象 ExecutorService pool = Executors.newFixedThreadPool(2); // 可以执行Runnable对象或者Callable对象代表的线程 Future<Integer> f1 = pool.submit(new MyCallable(100)); Future<Integer> f2 = pool.submit(new MyCallable(200)); // V get() Integer i1 = f1.get(); Integer i2 = f2.get(); System.out.println(i1); System.out.println(i2); // 结束 pool.shutdown(); } }
匿名内部类使用多线程
匿名内部类方式使用多线程 new Thread(){ //代码块 }.start(); new Thread(new Runnable(){ //代码块 }).start();
定时器
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
Timer
public Timer();
public void schedule(TimerTask task, long delay);
public void schedule(TimerTask task, long delay, long period);
TimerTask
public abstract void run();
public boolean cancel();
Quartz是一个由Java编写的开源框架。
import java.util.Timer; import java.util.TimerTask; /* * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 * 依赖Timer和TimerTask这两个类: * Timer:定时 * public Timer() * public void schedule(TimerTask task,long delay) * public void schedule(TimerTask task,long delay,long period) * public void cancel() * TimerTask:任务 */ public class TimerDemo { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); // 3秒后执行爆炸任务 // t.schedule(new MyTask(), 3000); //结束任务 t.schedule(new MyTask(t), 3000); } } // 做一个任务 class MyTask extends TimerTask { private Timer t; public MyTask(){} public MyTask(Timer t){ this.t = t; } @Override public void run() { System.out.println("beng,爆炸了"); t.cancel(); } }
* 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 * 依赖Timer和TimerTask这两个类: * Timer:定时 * public Timer() * public void schedule(TimerTask task,long delay) * public void schedule(TimerTask task,long delay,long period) * public void cancel() * TimerTask:任务 */ public class TimerDemo2 { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸 t.schedule(new MyTask2(), 3000, 2000); } } // 做一个任务 class MyTask2 extends TimerTask { @Override public void run() { System.out.println("beng,爆炸了"); } }
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /* * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo) */ class DeleteFolder extends TimerTask { @Override public void run() { File srcFolder = new File("demo"); deleteFolder(srcFolder); } // 递归删除目录 public void deleteFolder(File srcFolder) { File[] fileArray = srcFolder.listFiles(); if (fileArray != null) { for (File file : fileArray) { if (file.isDirectory()) { deleteFolder(file); } else { System.out.println(file.getName() + ":" + file.delete()); } } System.out.println(srcFolder.getName() + ":" + srcFolder.delete()); } } } public class TimerTest { public static void main(String[] args) throws ParseException { Timer t = new Timer(); String s = "2014-11-27 15:45:00"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(s); t.schedule(new DeleteFolder(), d); } }