Java笔记(24):多线程(02)
1、JDK5之后的Lock锁的概述和使用
1 package cn.itcast_01; 2 3 import java.util.concurrent.locks.Lock; 4 import java.util.concurrent.locks.ReentrantLock; 5 6 public class SellTicket implements Runnable { 7 8 // 定义票 9 private int tickets = 100; 10 11 // 定义锁对象 12 private Lock lock = new ReentrantLock(); 13 14 @Override 15 public void run() { 16 while (true) { 17 try { 18 // 加锁 19 lock.lock(); 20 if (tickets > 0) { 21 try { 22 Thread.sleep(100); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.println(Thread.currentThread().getName() 27 + "正在出售第" + (tickets--) + "张票"); 28 } 29 } finally { 30 // 释放锁 31 lock.unlock(); 32 } 33 } 34 } 35 36 }
1 package cn.itcast_01; 2 /* 3 * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁, 4 * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。 5 * 6 * Lock: 7 * void lock(): 获取锁。 8 * void unlock():释放锁。 9 * ReentrantLock是Lock的实现类. 10 */ 11 public class SellTicketDemo { 12 public static void main(String[] args) { 13 // 创建资源对象 14 SellTicket st = new SellTicket(); 15 16 // 创建三个窗口 17 Thread t1 = new Thread(st, "窗口1"); 18 Thread t2 = new Thread(st, "窗口2"); 19 Thread t3 = new Thread(st, "窗口3"); 20 21 // 启动线程 22 t1.start(); 23 t2.start(); 24 t3.start(); 25 } 26 }
2、死锁问题概述和使用
1 package cn.itcast_02; 2 3 public class DieLock extends Thread { 4 5 private boolean flag; 6 7 public DieLock(boolean flag) { 8 this.flag = flag; 9 } 10 11 @Override 12 public void run() { 13 if (flag) { 14 synchronized (MyLock.objA) { 15 System.out.println("if objA"); 16 synchronized (MyLock.objB) { 17 System.out.println("if objB"); 18 } 19 } 20 } else { 21 synchronized (MyLock.objB) { 22 System.out.println("else objB"); 23 synchronized (MyLock.objA) { 24 System.out.println("else objA"); 25 } 26 } 27 } 28 } 29 }
1 package cn.itcast_02; 2 3 /* 4 * 同步的弊端: 5 * A:效率低 6 * B:容易产生死锁 7 * 8 * 死锁: 9 * 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。 10 * 11 * 举例: 12 * 中国人,美国人吃饭案例。 13 * 正常情况: 14 * 中国人:筷子两支 15 * 美国人:刀和叉 16 * 现在: 17 * 中国人:筷子1支,刀一把 18 * 美国人:筷子1支,叉一把 19 */ 20 public class DieLockDemo { 21 public static void main(String[] args) { 22 DieLock dl1 = new DieLock(true); 23 DieLock dl2 = new DieLock(false); 24 25 dl1.start(); 26 dl2.start(); 27 } 28 }
3、生产者消费者问题代码1
1 package cn.itcast_03; 2 3 public class Student { 4 String name; 5 int age; 6 }
1 package cn.itcast_03; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 7 public SetThread(Student s) { 8 this.s = s; 9 } 10 11 @Override 12 public void run() { 13 // Student s = new Student(); 14 s.name = "林青霞"; 15 s.age = 27; 16 } 17 18 }
1 package cn.itcast_03; 2 3 public class GetThread implements Runnable { 4 private Student s; 5 6 public GetThread(Student s) { 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 // Student s = new Student(); 13 System.out.println(s.name + "---" + s.age); 14 } 15 16 }
1 package cn.itcast_03; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 */ 16 public class StudentDemo { 17 public static void main(String[] args) { 18 //创建资源 19 Student s = new Student(); 20 21 //设置和获取的类 22 SetThread st = new SetThread(s); 23 GetThread gt = new GetThread(s); 24 25 //线程类 26 Thread t1 = new Thread(st); 27 Thread t2 = new Thread(gt); 28 29 //启动线程 30 t1.start(); 31 t2.start(); 32 } 33 }
4、生产者消费者题代码2并解决线程安全问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package cn.itcast_04; 2 3 public class Student { 4 String name; 5 int age; 6 }
1 package cn.itcast_04; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 private int x = 0; 7 8 public SetThread(Student s) { 9 this.s = s; 10 } 11 12 @Override 13 public void run() { 14 while (true) { 15 synchronized (s) { 16 if (x % 2 == 0) { 17 s.name = "林青霞";//刚走到这里,就被别人抢到了执行权 18 s.age = 27; 19 } else { 20 s.name = "刘意"; //刚走到这里,就被别人抢到了执行权 21 s.age = 30; 22 } 23 x++; 24 } 25 } 26 } 27 }
1 package cn.itcast_04; 2 3 public class GetThread implements Runnable { 4 private Student s; 5 6 public GetThread(Student s) { 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while (true) { 13 synchronized (s) { 14 System.out.println(s.name + "---" + s.age); 15 } 16 } 17 } 18 }
1 package cn.itcast_04; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 16 * A:同一个数据出现多次 17 * B:姓名和年龄不匹配 18 * 原因: 19 * A:同一个数据出现多次 20 * CPU的一点点时间片的执行权,就足够你执行很多次。 21 * B:姓名和年龄不匹配 22 * 线程运行的随机性 23 * 线程安全问题: 24 * A:是否是多线程环境 是 25 * B:是否有共享数据 是 26 * C:是否有多条语句操作共享数据 是 27 * 解决方案: 28 * 加锁。 29 * 注意: 30 * A:不同种类的线程都要加锁。 31 * B:不同种类的线程加的锁必须是同一把。 32 */ 33 public class StudentDemo { 34 public static void main(String[] args) { 35 //创建资源 36 Student s = new Student(); 37 38 //设置和获取的类 39 SetThread st = new SetThread(s); 40 GetThread gt = new GetThread(s); 41 42 //线程类 43 Thread t1 = new Thread(st); 44 Thread t2 = new Thread(gt); 45 46 //启动线程 47 t1.start(); 48 t2.start(); 49 } 50 }
5、生产者消费者之等待唤醒机制代码实现
1 package cn.itcast_05; 2 3 public class Student { 4 String name; 5 int age; 6 boolean flag; // 默认情况是没有数据,如果是true,说明有数据 7 }
1 package cn.itcast_05; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 private int x = 0; 7 8 public SetThread(Student s) { 9 this.s = s; 10 } 11 12 @Override 13 public void run() { 14 while (true) { 15 synchronized (s) { 16 //判断有没有 17 if(s.flag){ 18 try { 19 s.wait(); //t1等着,释放锁 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 if (x % 2 == 0) { 26 s.name = "林青霞"; 27 s.age = 27; 28 } else { 29 s.name = "刘意"; 30 s.age = 30; 31 } 32 x++; //x=1 33 34 //修改标记 35 s.flag = true; 36 //唤醒线程 37 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。 38 } 39 //t1有,或者t2有 40 } 41 } 42 }
package cn.itcast_05; 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(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(s.name + "---" + s.age); //林青霞---27 //刘意---30 //修改标记 s.flag = false; //唤醒线程 s.notify(); //唤醒t1 } } } }
1 package cn.itcast_05; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 16 * A:同一个数据出现多次 17 * B:姓名和年龄不匹配 18 * 原因: 19 * A:同一个数据出现多次 20 * CPU的一点点时间片的执行权,就足够你执行很多次。 21 * B:姓名和年龄不匹配 22 * 线程运行的随机性 23 * 线程安全问题: 24 * A:是否是多线程环境 是 25 * B:是否有共享数据 是 26 * C:是否有多条语句操作共享数据 是 27 * 解决方案: 28 * 加锁。 29 * 注意: 30 * A:不同种类的线程都要加锁。 31 * B:不同种类的线程加的锁必须是同一把。 32 * 33 * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 34 * 如何实现呢? 35 * 通过Java提供的等待唤醒机制解决。 36 * 37 * 等待唤醒: 38 * Object类中提供了三个方法: 39 * wait():等待 40 * notify():唤醒单个线程 41 * notifyAll():唤醒所有线程 42 * 为什么这些方法不定义在Thread类中呢? 43 * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 44 * 所以,这些方法必须定义在Object类中。 45 */ 46 public class StudentDemo { 47 public static void main(String[] args) { 48 //创建资源 49 Student s = new Student(); 50 51 //设置和获取的类 52 SetThread st = new SetThread(s); 53 GetThread gt = new GetThread(s); 54 55 //线程类 56 Thread t1 = new Thread(st); 57 Thread t2 = new Thread(gt); 58 59 //启动线程 60 t1.start(); 61 t2.start(); 62 } 63 }
线程的状态转换图及常见执行情况
6、线程组的概述和使用
1 package cn.itcast_06; 2 3 public class MyRunnable implements Runnable { 4 5 @Override 6 public void run() { 7 for (int x = 0; x < 100; x++) { 8 System.out.println(Thread.currentThread().getName() + ":" + x); 9 } 10 } 11 12 }
1 package cn.itcast_06; 2 3 /* 4 * 线程组: 把多个线程组合到一起。 5 * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 6 */ 7 public class ThreadGroupDemo { 8 public static void main(String[] args) { 9 // method1(); 10 11 // 我们如何修改线程所在的组呢? 12 // 创建一个线程组 13 // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组 14 method2(); 15 16 // t1.start(); 17 // t2.start(); 18 } 19 20 private static void method2() { 21 // ThreadGroup(String name) 22 ThreadGroup tg = new ThreadGroup("这是一个新的组"); 23 24 MyRunnable my = new MyRunnable(); 25 // Thread(ThreadGroup group, Runnable target, String name) 26 Thread t1 = new Thread(tg, my, "林青霞"); 27 Thread t2 = new Thread(tg, my, "刘意"); 28 29 System.out.println(t1.getThreadGroup().getName()); 30 System.out.println(t2.getThreadGroup().getName()); 31 32 //通过组名称设置后台线程,表示该组的线程都是后台线程 33 tg.setDaemon(true); 34 } 35 36 private static void method1() { 37 MyRunnable my = new MyRunnable(); 38 Thread t1 = new Thread(my, "林青霞"); 39 Thread t2 = new Thread(my, "刘意"); 40 // 我不知道他们属于那个线程组,我想知道,怎么办 41 // 线程类里面的方法:public final ThreadGroup getThreadGroup() 42 ThreadGroup tg1 = t1.getThreadGroup(); 43 ThreadGroup tg2 = t2.getThreadGroup(); 44 // 线程组里面的方法:public final String getName() 45 String name1 = tg1.getName(); 46 String name2 = tg2.getName(); 47 System.out.println(name1); 48 System.out.println(name2); 49 // 通过结果我们知道了:线程默认情况下属于main线程组 50 // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组 51 System.out.println(Thread.currentThread().getThreadGroup().getName()); 52 } 53 }
7、生产者消费者之等待唤醒机制代码优化
1 package cn.itcast_07; 2 3 public class Student { 4 private String name; 5 private int age; 6 private boolean flag; // 默认情况是没有数据,如果是true,说明有数据 7 8 public synchronized void set(String name, int age) { 9 // 如果有数据,就等待 10 if (this.flag) { 11 try { 12 this.wait(); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 } 17 18 // 设置数据 19 this.name = name; 20 this.age = age; 21 22 // 修改标记 23 this.flag = true; 24 this.notify(); 25 } 26 27 public synchronized void get() { 28 // 如果没有数据,就等待 29 if (!this.flag) { 30 try { 31 this.wait(); 32 } catch (InterruptedException e) { 33 e.printStackTrace(); 34 } 35 } 36 37 // 获取数据 38 System.out.println(this.name + "---" + this.age); 39 40 // 修改标记 41 this.flag = false; 42 this.notify(); 43 } 44 }
1 package cn.itcast_07; 2 3 public class SetThread implements Runnable { 4 5 private Student s; 6 private int x = 0; 7 8 public SetThread(Student s) { 9 this.s = s; 10 } 11 12 @Override 13 public void run() { 14 while (true) { 15 if (x % 2 == 0) { 16 s.set("林青霞", 27); 17 } else { 18 s.set("刘意", 30); 19 } 20 x++; 21 } 22 } 23 }
1 package cn.itcast_07; 2 3 public class GetThread implements Runnable { 4 private Student s; 5 6 public GetThread(Student s) { 7 this.s = s; 8 } 9 10 @Override 11 public void run() { 12 while (true) { 13 s.get(); 14 } 15 } 16 }
1 package cn.itcast_07; 2 3 /* 4 * 分析: 5 * 资源类:Student 6 * 设置学生数据:SetThread(生产者) 7 * 获取学生数据:GetThread(消费者) 8 * 测试类:StudentDemo 9 * 10 * 问题1:按照思路写代码,发现数据每次都是:null---0 11 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 12 * 如何实现呢? 13 * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 14 * 15 * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 16 * A:同一个数据出现多次 17 * B:姓名和年龄不匹配 18 * 原因: 19 * A:同一个数据出现多次 20 * CPU的一点点时间片的执行权,就足够你执行很多次。 21 * B:姓名和年龄不匹配 22 * 线程运行的随机性 23 * 线程安全问题: 24 * A:是否是多线程环境 是 25 * B:是否有共享数据 是 26 * C:是否有多条语句操作共享数据 是 27 * 解决方案: 28 * 加锁。 29 * 注意: 30 * A:不同种类的线程都要加锁。 31 * B:不同种类的线程加的锁必须是同一把。 32 * 33 * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 34 * 如何实现呢? 35 * 通过Java提供的等待唤醒机制解决。 36 * 37 * 等待唤醒: 38 * Object类中提供了三个方法: 39 * wait():等待 40 * notify():唤醒单个线程 41 * notifyAll():唤醒所有线程 42 * 为什么这些方法不定义在Thread类中呢? 43 * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 44 * 所以,这些方法必须定义在Object类中。 45 * 46 * 最终版代码中: 47 * 把Student的成员变量给私有的了。 48 * 把设置和获取的操作给封装成了功能,并加了同步。 49 * 设置或者获取的线程里面只需要调用方法即可。 50 */ 51 public class StudentDemo { 52 public static void main(String[] args) { 53 //创建资源 54 Student s = new Student(); 55 56 //设置和获取的类 57 SetThread st = new SetThread(s); 58 GetThread gt = new GetThread(s); 59 60 //线程类 61 Thread t1 = new Thread(st); 62 Thread t2 = new Thread(gt); 63 64 //启动线程 65 t1.start(); 66 t2.start(); 67 } 68 }
8、线程池的概述和使用
1 package cn.itcast_08; 2 3 public class MyRunnable implements Runnable { 4 5 @Override 6 public void run() { 7 for (int x = 0; x < 100; x++) { 8 System.out.println(Thread.currentThread().getName() + ":" + x); 9 } 10 } 11 12 }
1 package cn.itcast_08; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 6 /* 7 * 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 8 * 9 * 如何实现线程的代码呢? 10 * A:创建一个线程池对象,控制要创建几个线程对象。 11 * public static ExecutorService newFixedThreadPool(int nThreads) 12 * B:这种线程池的线程可以执行: 13 * 可以执行Runnable对象或者Callable对象代表的线程 14 * 做一个类实现Runnable接口。 15 * C:调用如下方法即可 16 * Future<?> submit(Runnable task) 17 * <T> Future<T> submit(Callable<T> task) 18 * D:我就要结束,可以吗? 19 * 可以。 20 */ 21 public class ExecutorsDemo { 22 public static void main(String[] args) { 23 // 创建一个线程池对象,控制要创建几个线程对象。 24 // public static ExecutorService newFixedThreadPool(int nThreads) 25 ExecutorService pool = Executors.newFixedThreadPool(2); 26 27 // 可以执行Runnable对象或者Callable对象代表的线程 28 pool.submit(new MyRunnable()); 29 pool.submit(new MyRunnable()); 30 31 //结束线程池 32 pool.shutdown(); 33 } 34 }
9、多线程方式3的思路及代码实现
1 package cn.itcast_09; 2 3 import java.util.concurrent.Callable; 4 5 //Callable:是带泛型的接口。 6 //这里指定的泛型其实是call()方法的返回值类型。 7 public class MyCallable implements Callable { 8 9 @Override 10 public Object call() throws Exception { 11 for (int x = 0; x < 100; x++) { 12 System.out.println(Thread.currentThread().getName() + ":" + x); 13 } 14 return null; 15 } 16 17 }
1 package cn.itcast_09; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 6 /* 7 * 多线程实现的方式3: 8 * A:创建一个线程池对象,控制要创建几个线程对象。 9 * public static ExecutorService newFixedThreadPool(int nThreads) 10 * B:这种线程池的线程可以执行: 11 * 可以执行Runnable对象或者Callable对象代表的线程 12 * 做一个类实现Runnable接口。 13 * C:调用如下方法即可 14 * Future<?> submit(Runnable task) 15 * <T> Future<T> submit(Callable<T> task) 16 * D:我就要结束,可以吗? 17 * 可以。 18 */ 19 public class CallableDemo { 20 public static void main(String[] args) { 21 //创建线程池对象 22 ExecutorService pool = Executors.newFixedThreadPool(2); 23 24 //可以执行Runnable对象或者Callable对象代表的线程 25 pool.submit(new MyCallable()); 26 pool.submit(new MyCallable()); 27 28 //结束 29 pool.shutdown(); 30 } 31 }
练习:多线程方式3的求和案例
1 package cn.itcast_10; 2 3 import java.util.concurrent.Callable; 4 5 /* 6 * 线程求和案例 7 */ 8 public class MyCallable implements Callable<Integer> { 9 10 private int number; 11 12 public MyCallable(int number) { 13 this.number = number; 14 } 15 16 @Override 17 public Integer call() throws Exception { 18 int sum = 0; 19 for (int x = 1; x <= number; x++) { 20 sum += x; 21 } 22 return sum; 23 } 24 25 }
1 package cn.itcast_10; 2 3 import java.util.concurrent.ExecutionException; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 import java.util.concurrent.Future; 7 8 /* 9 * 多线程实现的方式3: 10 * A:创建一个线程池对象,控制要创建几个线程对象。 11 * public static ExecutorService newFixedThreadPool(int nThreads) 12 * B:这种线程池的线程可以执行: 13 * 可以执行Runnable对象或者Callable对象代表的线程 14 * 做一个类实现Runnable接口。 15 * C:调用如下方法即可 16 * Future<?> submit(Runnable task) 17 * <T> Future<T> submit(Callable<T> task) 18 * D:我就要结束,可以吗? 19 * 可以。 20 */ 21 public class CallableDemo { 22 public static void main(String[] args) throws InterruptedException, ExecutionException { 23 // 创建线程池对象 24 ExecutorService pool = Executors.newFixedThreadPool(2); 25 26 // 可以执行Runnable对象或者Callable对象代表的线程 27 Future<Integer> f1 = pool.submit(new MyCallable(100)); 28 Future<Integer> f2 = pool.submit(new MyCallable(200)); 29 30 // V get() 31 Integer i1 = f1.get(); 32 Integer i2 = f2.get(); 33 34 System.out.println(i1); 35 System.out.println(i2); 36 37 // 结束 38 pool.shutdown(); 39 } 40 }
10、匿名内部类的方式实现多线程程序
1 package cn.itcast_11; 2 3 /* 4 * 匿名内部类的格式: 5 * new 类名或者接口名() { 6 * 重写方法; 7 * }; 8 * 本质:是该类或者接口的子类对象。 9 */ 10 public class ThreadDemo { 11 public static void main(String[] args) { 12 // 继承Thread类来实现多线程 13 new Thread() { 14 public void run() { 15 for (int x = 0; x < 100; x++) { 16 System.out.println(Thread.currentThread().getName() + ":" 17 + x); 18 } 19 } 20 }.start(); 21 22 // 实现Runnable接口来实现多线程 23 new Thread(new Runnable() { 24 @Override 25 public void run() { 26 for (int x = 0; x < 100; x++) { 27 System.out.println(Thread.currentThread().getName() + ":" 28 + x); 29 } 30 } 31 }) { 32 }.start(); 33 34 // 更有难度的 35 new Thread(new Runnable() { 36 @Override 37 public void run() { 38 for (int x = 0; x < 100; x++) { 39 System.out.println("hello" + ":" + x); 40 } 41 } 42 }) { 43 public void run() { 44 for (int x = 0; x < 100; x++) { 45 System.out.println("world" + ":" + x); 46 } 47 } 48 }.start(); 49 } 50 }
11、定时器的概述和使用
1 package cn.itcast_12; 2 3 import java.util.Timer; 4 import java.util.TimerTask; 5 6 /* 7 * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 8 * 依赖Timer和TimerTask这两个类: 9 * Timer:定时 10 * public Timer() 11 * public void schedule(TimerTask task,long delay) 12 * public void schedule(TimerTask task,long delay,long period) 13 * public void cancel() 14 * TimerTask:任务 15 */ 16 public class TimerDemo { 17 public static void main(String[] args) { 18 // 创建定时器对象 19 Timer t = new Timer(); 20 // 3秒后执行爆炸任务 21 // t.schedule(new MyTask(), 3000); 22 //结束任务 23 t.schedule(new MyTask(t), 3000); 24 } 25 } 26 27 // 做一个任务 28 class MyTask extends TimerTask { 29 30 private Timer t; 31 32 public MyTask(){} 33 34 public MyTask(Timer t){ 35 this.t = t; 36 } 37 38 @Override 39 public void run() { 40 System.out.println("beng,爆炸了"); 41 t.cancel(); 42 } 43 44 }
练习:定时任务的多次执行代码体现
1 package cn.itcast_12; 2 3 import java.util.Timer; 4 import java.util.TimerTask; 5 6 /* 7 * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 8 * 依赖Timer和TimerTask这两个类: 9 * Timer:定时 10 * public Timer() 11 * public void schedule(TimerTask task,long delay) 12 * public void schedule(TimerTask task,long delay,long period) 13 * public void cancel() 14 * TimerTask:任务 15 */ 16 public class TimerDemo2 { 17 public static void main(String[] args) { 18 // 创建定时器对象 19 Timer t = new Timer(); 20 // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸 21 t.schedule(new MyTask2(), 3000, 2000); 22 } 23 } 24 25 // 做一个任务 26 class MyTask2 extends TimerTask { 27 @Override 28 public void run() { 29 System.out.println("beng,爆炸了"); 30 } 31 }
练习:定时删除指定的带内容目录
1 package cn.itcast_12; 2 3 import java.io.File; 4 import java.text.ParseException; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 import java.util.Timer; 8 import java.util.TimerTask; 9 10 /* 11 * 需求:在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo) 12 */ 13 14 class DeleteFolder extends TimerTask { 15 16 @Override 17 public void run() { 18 File srcFolder = new File("demo"); 19 deleteFolder(srcFolder); 20 } 21 22 // 递归删除目录 23 public void deleteFolder(File srcFolder) { 24 File[] fileArray = srcFolder.listFiles(); 25 if (fileArray != null) { 26 for (File file : fileArray) { 27 if (file.isDirectory()) { 28 deleteFolder(file); 29 } else { 30 System.out.println(file.getName() + ":" + file.delete()); 31 } 32 } 33 System.out.println(srcFolder.getName() + ":" + srcFolder.delete()); 34 } 35 } 36 } 37 38 public class TimerTest { 39 public static void main(String[] args) throws ParseException { 40 Timer t = new Timer(); 41 42 String s = "2014-11-27 15:45:00"; 43 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 44 Date d = sdf.parse(s); 45 46 t.schedule(new DeleteFolder(), d); 47 } 48 }
12、多线程总结
/* * 1:多线程有几种实现方案,分别是哪几种? * 两种。 * 继承Thread类 * 实现Runnable接口 * 扩展一种:实现Callable接口。这个得和线程池结合。 * * 2:同步有几种方式,分别是什么? * 两种。 * 同步代码块 * 同步方法 * 3:启动一个线程是run()还是start()?它们的区别? * start(); * run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用 * start():启动线程,并由JVM自动调用run()方法 * 4:sleep()和wait()方法的区别 * sleep():必须指时间;不释放锁。 * wait():可以不指定时间,也可以指定时间;释放锁。 * 5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中 * 因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。 * 而Object代码任意的对象,所以,定义在这里面。 * 6:线程的生命周期图 * 新建 -- 就绪 -- 运行 -- 死亡 * 新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡 */
--干了每滴寂寞 进化成更好的我 等着你 在我世界 路过... ...
如欢如殇 授以青春鲜活肢体奔忙
如思如忘 驱以老朽深沉灵魂冥想
始自情热激荡 从未敢终于世事炎凉
无能执手相望 无法去尝试结发同床
无力至心死身僵 一息坚强
------
我一直没有放弃,如果你也能看到
修身 修禅