一、JDK5之后的Lock锁的概述和使用

package a;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
	//定义票
	private int tickets = 100;
	
	//定义锁对象
	private Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		while(true){
			try {
				//加锁
				lock.lock();
				if(tickets > 0){
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
				}
			} finally {
				//释放锁
				lock.unlock();
			}
			
		}

	}

}
package a;
/**
 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上锁,在哪里释放锁
 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
 * 
 *  public void lock() 获取锁
 *  public void unlock()释放锁
 *
 */
public class SellTicketDemo {
	public static void main(String[] args) {
		SellTicket s = new SellTicket();
		
		Thread t1 = new Thread(s,"窗口1");
		Thread t2 = new Thread(s,"窗口2");
		Thread t3 = new Thread(s,"窗口3");
		
		t1.start();
		t2.start();
		t3.start();
		
	}
}


二、死锁

同步的弊端:

  1.效率低,但是安全,我接收。

  2.如果出现了同步弊端,就容易产生死锁问题。比如,现在有一个单人卫生间,一个人拉肚子进去了,然后将门给锁上。那么其他人如果要进去,只能等先前那个人出来,但是那人一直在里面就是不出来(痛并快乐着),那么其他人只能等。


死锁问题及其代码:

  是指两个或者两个以上的线程在执行的过程中,因为争夺资源产生的一种互相等待现象。

package b;

public class MyLock {
	//创建两个锁对象
	public  static final Object objA = new Object();
	public  static final Object objB = new Object();
}
package b;

public class DieLock extends Thread{
	private boolean flag ;
	public DieLock(boolean flag) {
		this.flag = flag;
	}
	
	@Override
	public void run() {
		if(flag){
			synchronized (MyLock.objA) {
				System.out.println("if objA");
				synchronized (MyLock.objB) {
					System.out.println("if objB");
				}
			}
			
		}else{
			synchronized (MyLock.objB) {
				System.out.println("else objB");
				synchronized (MyLock.objA) {
					System.out.println("else objA");
				}
			}
		}
	}
}
package b;

public class DieLockDemo {
	
	public static void main(String[] args) {
		DieLock dl1 = new DieLock(true);
		DieLock dl2 = new DieLock(false);
		
		dl1.start();
		dl2.start();
		
	}

}

运行结果1:

if objA

if objB

else objB

else objA

运行结果2:

if objA

else objB

运行结果3:

else objB

if objA


分析运行结果1:

  当线程1调用start()和线程2调用start()方法时,线程1正好抢到CPU的执行权,那么此时会执行if语句,输出if objA,但是此时线程2依然没有抢到CPU的执行权,那么此时线程1继续执行,输出if objB。此时线程1执行完毕,线程2抢到CPU执行权,然后依次输出else objB,else objB。


分析运行结果2:

  当线程1调用start()和线程2调用start()方法时,线程1正好抢到CPU的执行权,那么此时会执行if语句,输出if objA,锁对象objA没有释放,但是此时线程2抢到CPU的执行权,输出else ojbB,也没有释放锁对象objB,然后此时线程1抢到了CPU的执行权,那么要加锁时,却发现锁对象ojbB没有释放,就等待线程2释放锁对象ojbB,但是线程2就不释放锁对象ojbB。


分析运行结果3:

  参考分析运行结果2.


三、线程间通信

  针对同一个资源的操作有不同种类的线程。例如:卖票有进也有出。

package cn1;

public class Student {
	String name;//学生姓名
	int age;//学生年龄
	

}
package cn1;

public class SetThread implements Runnable {

	@Override
	public void run() {
		Student s = new Student();
		s.name = "林青霞";
		s.age = 27; 

	}

}
package cn1;

public class GetThread implements Runnable {

	@Override
	public void run() {
		Student s = new Student();
		System.out.println("姓名是:"+s.name+",学生年龄:"+s.age);

	}

}
package cn1;
/**
 * 分析:
 * 		资源类:Student
 * 		设置学生数据:SetThread   生产者
 * 		获取学生数据:getThread   消费者
 * 		测试类:StudentDemo
 */
public class StudentDemo {
	public static void main(String[] args) {
		SetThread st = new SetThread();
		GetThread gt = new GetThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
	}

}

姓名是:null,学生年龄:0

  原因是我们再每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个。

如何实现?

  在外界把这个数据创建出来,通过构造方法传递给其他的类。

package cn1;

public class Student {
	String name;//学生姓名
	int age;//学生年龄
	

}
package cn1;

public class SetThread implements Runnable {
	private Student s;
	
	public SetThread(Student s){
		this.s = s;
	}
		
	@Override
	public void run() {
		s.name = "林青霞";
		s.age = 27; 

	}

}
package cn1;

public class GetThread implements Runnable {
	private Student s;
	public GetThread(Student s){
		this.s = s;
	}
	
	@Override
	public void run() {
		System.out.println("姓名是:"+s.name+",学生年龄:"+s.age);

	}

}
package cn1;
/**
 * 分析:
 * 		资源类:Student
 * 		设置学生数据:SetThread   生产者
 * 		获取学生数据:getThread   消费者
 * 		测试类:StudentDemo
 */
public class StudentDemo {
	public static void main(String[] args) {
		//创建资源
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
	}

}

姓名是:林青霞,学生年龄:27

但是也有可能结果是这样的:姓名是:null,学生年龄:0。或者是这样的结果::姓名是:null,学生年龄:27。

为什么?

  姓名是:林青霞,学生年龄:27?

  原因:是线程1先获得CPU的执行权(设置属性值),接着线程2获得CPU的执行权(获得属性值)。

  姓名是:null,学生年龄:0?

  原因:是线程2先获得CPU的执行权(取得属性值),但是此时线程1还没有取得CPU的执行权,所以呢,值就为默认值啦。

  其他的原因就不去推测了,自己想想就知道啦??

package cn;

public class Student {
	String name;
	int age;
 }
package cn;

public class SetThread implements Runnable{

	private Student s;
	private int x = 0;
	public SetThread(Student s){
		this.s = s;
	}
	public void run() {
		while(true){
			if(x % 2 == 0 ){
				s.name = "林青霞";
				s.age = 20;
			}else{
				s.name = "东方不败";
				s.age = 30;
			}
			x++;
		}
		
		
	}
	

}
package cn;

public class GetThread implements Runnable {
	private Student s ;
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			System.out.println("姓名:"+s.name+",年龄:"+s.age);
		}

	}

}
package cn;
/**
 * 为了数据的效果好一些,就加入了循环和判断,给出了不同的值,这个时候就产生了不同的问题
 * 		同一个数据出现了很多次:CPU的一点点时间片的执行权,就足够执行很多次
 * 		姓名和年龄不匹配:线程运行的随机性
 * 	
 *
 */
public class StudentDemo {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
		
		
	}

}

姓名:东方不败,年龄:20

姓名:东方不败,年龄:30

姓名:东方不败,年龄:20

姓名:林青霞,年龄:30

姓名:东方不败,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:30

姓名:东方不败,年龄:30

姓名:林青霞,年龄:30

姓名:林青霞,年龄:20

姓名:林青霞,年龄:30

姓名:东方不败,年龄:20

姓名:东方不败,年龄:20


我们发现同一个数据出现了多次以及姓名和年龄不匹配,原因已经在上面给出了。


那么如何解决呢?加锁

package cn;

public class Student {
	String name;
	int age;
 }
package cn;

public class SetThread implements Runnable{

	private Student s;
	private int x = 0;
	public SetThread(Student s){
		this.s = s;
	}
	public void run() {
		while(true){
			synchronized (new Object()) {
				if(x % 2 == 0 ){
					s.name = "林青霞";
					s.age = 20;
				}else{
					s.name = "东方不败";
					s.age = 30;
				}
				x++;
			}
		}
		
		
	}
	

}
package cn;

public class GetThread implements Runnable {
	private Student s ;
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			synchronized (new Object()) {
				System.out.println("姓名:"+s.name+",年龄:"+s.age);
			}
		}
	}

}
package cn;
/**
 * 为了数据的效果好一些,就加入了循环和判断,给出了不同的值,这个时候就产生了不同的问题
 * 		同一个数据出现了很多次:CPU的一点点时间片的执行权,就足够执行很多次
 * 		姓名和年龄不匹配:线程运行的随机性
 * 	
 * 线程安全问题:
 * 		是否是多线程环境	是
 * 		是否有共享数据	是
 * 		是否有多条语句操作共享数据		是
 * 解决方案:
 * 		加锁 	不同种类的线程都要加锁
 */
public class StudentDemo {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
		
		
	}

}

姓名:林青霞,年龄:20

姓名:林青霞,年龄:20

姓名:东方不败,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:林青霞,年龄:30

姓名:林青霞,年龄:30

姓名:林青霞,年龄:30

姓名:东方不败,年龄:30

姓名:林青霞,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:20

姓名:林青霞,年龄:20

姓名:林青霞,年龄:20

姓名:林青霞,年龄:30

姓名:林青霞,年龄:30

姓名:东方不败,年龄:20

姓名:林青霞,年龄:20

  结果怎么还是这样?那是因为我们加的锁不一样。

wKiom1gpcciRN3nJAAAsL9q3ONA039.png

wKiom1gpcdWQyOFMAAAP7UY9W3A065.png

package cn;

public class Student {
	String name;
	int age;
 }
package cn;

public class SetThread implements Runnable{

	private Student s;
	private int x = 0;
	public SetThread(Student s){
		this.s = s;
	}
	public void run() {
		while(true){
			synchronized (s) {
				if(x % 2 == 0 ){
					s.name = "林青霞";
					s.age = 20;
				}else{
					s.name = "东方不败";
					s.age = 30;
				}
				x++;
			}
		}
		
		
	}
	

}
package cn;

public class GetThread implements Runnable {
	private Student s ;
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			synchronized (s) {
				System.out.println("姓名:"+s.name+",年龄:"+s.age);
			}
		}
	}

}
package cn;
/**
 * 为了数据的效果好一些,就加入了循环和判断,给出了不同的值,这个时候就产生了不同的问题
 * 		同一个数据出现了很多次:CPU的一点点时间片的执行权,就足够执行很多次
 * 		姓名和年龄不匹配:线程运行的随机性
 * 	
 * 线程安全问题:
 * 		是否是多线程环境	是
 * 		是否有共享数据	是
 * 		是否有多条语句操作共享数据		是
 * 解决方案:
 * 		加锁 	不同种类的线程都要加锁,而且不同种类的线程必须加同一把锁
 */
public class StudentDemo {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
		
		
	}

}

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

姓名:东方不败,年龄:30

上述的仅仅解决了数据的匹配问题,但是每生产一条数据,就消费一条数据(生产者和消费者),即,当线程1设置了对象的属性,线程2就输出对象的属性。

wKioL1gpdoijlqaJAABG2effh5E941.png

package cn;

public class Student {
	String name;
	int age;
	boolean flag;//默认没有数据
 }
package cn;

public class SetThread implements Runnable{

	private Student s;
	private int x = 0;
	public SetThread(Student s){
		this.s = s;
	}
	public void run() {
		while(true){
			synchronized (s) {
				//如果有数据
				if(s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else{//数据没有数据
					if(x % 2 == 0 ){
						s.name = "林青霞";
						s.age = 20;
					}else{
						s.name = "东方不败";
						s.age = 30;
					}
					x++;
					s.flag = true;
					s.notify();
				}
			}
		}
		
		
	}
	

}
package cn;

public class GetThread implements Runnable {
	private Student s ;
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			synchronized (s) {
				//如果没有数据
				if(!s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}else{//如果有数据
					System.out.println("姓名:"+s.name+",年龄:"+s.age);
					s.flag = false;
					s.notify();
				}
			}
		}
	}

}
package cn;
public class StudentDemo {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t2.start();
		t1.start();
		
		
	}

}

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

姓名:林青霞,年龄:20

姓名:东方不败,年龄:30

  

等待唤醒的原理是什么呢?

  等线程1和线程2启动的时候,假设此时线程2抢到CPU的执行权,那么执行线程2的run()方法,并执行加锁,但是此时s.flag为false,取反之后为true,然后执行等待,等待的同时释放锁,并且下次一旦其他线程唤醒线程1,会从这边继续执行。

  那么此时线程2获取CPU的执行权,并执行加锁,这个时候s.flag是false,那么会进入else语句,又因为x=0,可以整除2,那么会执行s.name="林青霞",s.age="20",然后将flag变为true,然后执行唤醒,但是此时锁并没有释放,然后else执行完毕,释放锁。

  之后,线程1和线程2继续争夺CPU的执行权,如果此时线程1获得CPU的执行权,并加锁,又因为s.flag=true,所以执行if语句里的代码,然后线程1等待,并释放锁,那么此时只有线程2来获得CPU的执行权了,那么线程2唤醒了,然后判断s.flag=true,那么就输出姓名:林青霞,年龄:20,将s.flag=false,然后通知其他线程,else执行完毕之后,释放锁,那么线程1和线程2继续争夺CPU的执行权。


四、线程的状态

wKioL1gpm9mx-FQnAADdXj5VbN8923.png


五、线程组

  java中使用ThreadGroup来表示线程组。它可以对一批线程进行分类管理,java允许程序直接对线程组进行控制。

  默认情况下,所有的线程都属于主线程组。public final ThreadGroup getThreadGroup()

  我们也可以给线程设置分组:Thread(ThreadGroup group,Runnable target,String name)

package cn1;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}

	}

}
package cn1;
/**
 * 线程组:把多个线程组合到一起
 * 		它可以对一批线程进行分类管理,java允许程序直接对线程组控制
 * 
 *	public ThreadGroup getThreadGroup()获取默认的线程组
 *	
 */
public class ThreadGroupDemo {
	public static void main(String[] args) {
		MyRunnable my = new MyRunnable();
		
		Thread t1 = new Thread(my, "林青霞");
		Thread t2 = new Thread(my, "东方不败");
		
		ThreadGroup tg1 = t1.getThreadGroup();
		System.out.println(tg1.getName());
		
		ThreadGroup tg2 = t2.getThreadGroup();
		System.out.println(tg2.getName());
		
		System.out.println(Thread.currentThread().getThreadGroup().getName());
	}
}

main

main

main

  通过结果我们知道,线程默认情况下属于main线程组。所有的线程都属于同一线程组,即main线程组。

package cn1;
/**
 * 线程组:把多个线程组合到一起
 * 		它可以对一批线程进行分类管理,java允许程序直接对线程组控制
 * 
 *	public ThreadGroup getThreadGroup()获取默认的线程组
 *	
 *
 *
 *	
 */
public class ThreadGroupDemo {
	public static void main(String[] args) {
		method1();
		
		method2();
		
		
		
	}
	/**
	 * 给线程设置分组
	 */
	private static void method2() {
		ThreadGroup tg = new ThreadGroup("这是一个新的组");
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(tg, my, "线程1");
		Thread t2 = new Thread(tg, my, "线程2");
		
		System.out.println("t1的线程组名称为:"+t1.getThreadGroup().getName());
		System.out.println("t2的线程组名称为:"+t2.getThreadGroup().getName());
		
	}
	/**
	 * 获取默认的线程组
	 */
	private static void method1() {
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my, "林青霞");
		Thread t2 = new Thread(my, "东方不败");
		
		ThreadGroup tg1 = t1.getThreadGroup();
		System.out.println(tg1.getName());
		
		ThreadGroup tg2 = t2.getThreadGroup();
		System.out.println(tg2.getName());
		
		System.out.println(Thread.currentThread().getThreadGroup().getName());
		
	}
}

main

main

main

t1的线程组名称为:这是一个新的组

t2的线程组名称为:这是一个新的组


六、生产者和消费者值等待唤醒机制代码的优化

package cn2;

public class Student {
	private String name;
	private int age;
	private boolean flag;
	
	public synchronized void set(String name,int age){
		//如果有数据
		if(this.flag){
			//等待
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//设置数据
		this.name = name;
		this.age = age;
		this.flag = true;
		this.notify();//唤醒线程
		
			
		
	}
	
	public synchronized void get(){
		//如果没有数据,就等待
		if(!this.flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name+":"+this.age);
		this.flag = false;
		this.notify();////唤醒线程
			
		
	}
}
package cn2;

public class SetThread implements Runnable {
	private Student s;
	private int x = 0;
	
	public SetThread(Student s){
		this.s = s;
	}
	
	@Override
	public void run() {
		while(true){
			if(x % 2 == 0){
				s.set("林青霞", 20);
			}else{
				s.set("东方不败", 30);
			}
			x++;
		}

	}

}
package cn2;

public class GetThread implements Runnable{
	
	private Student s;
	
	public GetThread(Student s){
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			s.get();
		}
		
	}
	
	

}
package cn2;

public class StudentDemo {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		
		t1.start();
		t2.start();
	}

}


七、线程池

  程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

  线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

  在JDK5之前,我们必须手动实现自己的线程池,从JDK5之后,java内置支持线程池。

  JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法:

public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ExecutorService newFixedThreadPool(int nThreads)

  这些方法的返回值是ExecutorService对象,该对象表示一个线程池么可以执行Runnable或Callable对象代表的线程。它提供了如下方法

<T> Future<T> submit(Callable<T> task)
Future<?> submit(Runnable task)
package cn2;

public class MyThread implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"--"+i);
		}

	}

}
package cn2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsDemo {
	public static void main(String[] args) {
		//创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		MyThread my = new MyThread();
		//创建Runnable实例
		Thread t1 = new Thread(my);
		Thread t2 = new Thread(my);
		//提交Runnable实例
		pool.submit(t1);
		pool.submit(t2);
		
		//关闭线程池
		pool.shutdown();
	}
}

 

八、多线程实现方式三

package cn3;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
	private int from;
	private int to;
	public MyCallable(int from ,int to){
		this.from = from;
		this.to = to;
	}

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = from; i <= to; i++) {
			sum += from + i;
		}
		return sum;
	}

}
package cn3;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		Future<Integer> f1 = pool.submit(new MyCallable(0, 100));
		Future<Integer> f2 = pool.submit(new MyCallable(0, 50));
		
		Integer t1 = f1.get();
		Integer t2 = f2.get();
		
		System.out.println(t1);
		System.out.println(t2);
		
		pool.shutdown();
	}

}


九、定时器

  定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在java中,可以通过Timer和TimerTask类来实现定义调度的功能。

  开发中:Quartz是一个完全由java编写的开源调度框架。 

package cn4;

import java.util.Timer;
import java.util.TimerTask;

class MyTimerTask extends TimerTask{

	@Override
	public void run() {
		System.out.println("爆炸");		
		
	}
	
}
public class TimerTest {
	public static void main(String[] args) {
		Timer t = new Timer();
		t.schedule(new MyTimerTask(), 3000);
	}
}

 wKiom1grAamRw7NHAAA3h3LPQ2M023.png

如何在3秒执行后终止定时器呢?

package cn4;

import java.util.Timer;
import java.util.TimerTask;

class MyTimerTask extends TimerTask{
	private Timer t;
	public MyTimerTask(Timer t) {
		this.t = t;
	}
	

	@Override
	public void run() {
		System.out.println("爆炸");
		t.cancel();//结束定时器
		
	}
	
}
public class TimerTest {
	public static void main(String[] args) {
		Timer t = new Timer();
		t.schedule(new MyTimerTask(t), 3000);
	}
}
package cn5;

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时删除文件夹
 *
 */
class DeleteFolder extends TimerTask{
	private Timer t ;
	public DeleteFolder(Timer t){
		this.t = t;
	}

	@Override
	public void run() {
		File srcFolder = new File("demo");
		deleteFolder(srcFolder);
		t.cancel();
	}
	//递归删除目录
	private void deleteFolder(File srcFolder) {
		File[] fileArray = srcFolder.listFiles();
		if(fileArray != null){
			for (File file : fileArray) {
				if(file.isDirectory()){
					deleteFolder(file);
				}else{
					file.delete();
				}
			}
			srcFolder.delete();
			
		}
		
	}
	
}
public class TimerTest {
	public static void main(String[] args) {
		Timer t = new Timer();
		t.schedule(new DeleteFolder(t), 3000);
	}
}