javaSE第二十四天
C:等待唤醒机制改进该程序,让数据能够实现依次的出现 372
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中 376
1. Student(资源类,把数据及操作都写在资源类中) 376
1. TimerDemo类(任务完成之后,可以终止定时器) 387
2. TimerDemo2类(任务完成之后,不终止定时器) 388
第二十四天
1:多线程(理解)
(1)JDK5以后的Lock锁
A:定义
JDK5以后的针对线程的锁定操作和释放操作(Lock是一个接口,具体实例由其实现类ReentrantLock)
B:方法:
实现类:ReenTrantlock的方法:
void lock():加锁
void unlock():解锁
C:具体应用(以售票程序为例)
1,. SellTicket类
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(); } } }
} |
2,. SellTicketDemo测试类
/* * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁, * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。 * * Lock: * void lock(): 获取锁。 * void unlock():释放锁。 * ReentrantLock是Lock的实现类. * 通过ReentrantLock类来创建锁对象 */ public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket();
// 创建三个窗口 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3");
// 启动线程 t1.start(); t2.start(); t3.start(); } } |
(2)死锁问题的描述和代码体现
1. DieLockDemo测试类
/* * 同步的弊端: * A:效率低 * B:容易产生死锁 * * 死锁: * 两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。 * * 举例: * 中国人,美国人吃饭案例。 * 正常情况: * 中国人:筷子两支 * 美国人:刀和叉 * 现在: 支,刀一把 支,叉一把 */ public class DieLockDemo { public static void main(String[] args) { DieLock dl1 = new DieLock(true); DieLock dl2 = new DieLock(false);
dl1.start(); dl2.start(); } } |
2. DieLock类(该类继承自Thread)
/** * 死锁演示 * @author asus1 * */ 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"); } } } } } |
3. MyLock(锁对象类)
/** * 死锁演示 * @author asus1 * */ public class MyLock { // 创建两把锁对象 public static final Object objA = new Object(); public static final Object objB = new Object(); } |
(3)生产者和消费者多线程体现(线程间通信问题)
以学生作为资源来实现的
资源类:Student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo
代码:
A:最基本的版本,只有一个数据。
1. Student资源类
/* * Student:资源类 */ public class Student { public String name; public int age; } |
2. SetThread(生产者类)
/* * 生产者类: */ public class SetThread implements Runnable { //保证每个线程是对同一个对象进行操作 private Student s;
public SetThread(Student s) { this.s = s; }
@Override public void run() { // Student s = new Student(); s.name = "林青霞"; s.age = 27; } } |
3. GetThread(消费者类)
/* * 消费者类 */ public class GetThread implements Runnable { //保证每个线程是对同一个对象进行操作 private Student s;
public GetThread(Student s) { this.s = s; }
@Override public void run() { // Student s = new Student(); System.out.println(s.name + "---" + s.age); } } |
4. StudentDemo测试类
/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * :按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * */ 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(); } } |
B:改进版本,给出了不同的数据,并加入了同步机制
1. Student资源类
/* * 资源类: */ public class Student { String name; int age; } |
2. SetThread(生产者类)
/* * 生产者类: */ 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) { //加入了同步代码块,要保证多个线程是同意把锁 synchronized (s) { if (x % 2 == 0) { s.name = "林青霞";//刚走到这里,就被别人抢到了执行权 s.age = 27; } else { s.name = "刘意"; //刚走到这里,就被别人抢到了执行权 s.age = 30; } x++; } } } } |
3. GetThread(消费者)
/* * 消费者类: */ 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); } } } } |
4. StudentDemo(测试类)
/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * :按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * :为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 * A:同一个数据出现多次 * B:姓名和年龄不匹配 * 原因: * A:同一个数据出现多次 * CPU的一点点时间片的执行权,就足够你执行很多次。 * B:姓名和年龄不匹配 * 线程运行的随机性 * 线程安全问题: * A:是否是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操作共享数据 是 * 解决方案: * 加锁。 * 注意: * A:不同种类的线程都要加锁。 * B:不同种类的线程加的锁必须是同一把。 */ 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(); } } |
C:等待唤醒机制改进该程序,让数据能够实现依次的出现
Object类的方法:
wait():让线程等待,并释放锁
notify():唤醒线程,并加锁
notifyAll() (多生产多消费)
1. Student(资源类)
/* * 资源类: */ public class Student { String name; int age; boolean flag; // 默认情况是没有数据,如果是true,说明有数据 } |
2. SetThread(生产者类)
/* * 生产者类:加入了等待-唤醒机制 */ 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) { synchronized (s) { //判断有没有 if(s.flag){ try { s.wait(); //t1等着,释放锁 } catch (InterruptedException e) { e.printStackTrace(); } }
if (x % 2 == 0) { s.name = "林青霞"; s.age = 27; } else { s.name = "刘意"; s.age = 30; } x++; //x=1
//修改标记 s.flag = true; //唤醒线程 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。 } //t1有,或者t2有 } } } |
3. GetThread(消费者类)
/* * 消费者类:加入了等待-唤醒机制 */ 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 } } } } |
4. StudentDemo(测试类)
/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * :按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * :为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 * A:同一个数据出现多次 * B:姓名和年龄不匹配 * 原因: * A:同一个数据出现多次 * CPU的一点点时间片的执行权,就足够你执行很多次。 * B:姓名和年龄不匹配 * 线程运行的随机性 * 线程安全问题: * A:是否是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操作共享数据 是 * 解决方案: * 加锁。 * 注意: * A:不同种类的线程都要加锁。 * B:不同种类的线程加的锁必须是同一把。 * * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 * 如何实现呢? * 通过Java提供的等待唤醒机制解决。 * * 等待唤醒: * Object类中提供了三个方法: * wait():等待 * notify():唤醒单个线程 * notifyAll():唤醒所有线程 * 为什么这些方法不定义在Thread类中呢? * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 * 所以,这些方法必须定义在Object类中。 */ 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(); } } |
D:等待唤醒机制的代码优化。把数据及操作都写在了资源类中
1. Student(资源类,把数据及操作都写在资源类中)
/* * 资源类:把数据及操作都写在资源类上面 */ public class Student { private String name; private int age; private boolean flag; // 默认情况是没有数据,如果是true,说明有数据
//使用同步方法解决线程安全问题 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(); } } |
2. SetThread(生产者类)
/* * 生产者类: */ 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("林青霞", 27); } else { s.set("刘意", 30); } x++; } } } |
3. GetThread(消费者类)
/* * 消费者类: */ public class GetThread implements Runnable { private Student s;
public GetThread(Student s) { this.s = s; }
@Override public void run() { while (true) { s.get(); } } } |
4. StudentDemo(测试类)
/* * 分析: * 资源类:Student * 设置学生数据:SetThread(生产者) * 获取学生数据:GetThread(消费者) * 测试类:StudentDemo * :按照思路写代码,发现数据每次都是:null---0 * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个 * 如何实现呢? * 在外界把这个数据创建出来,通过构造方法传递给其他的类。 * :为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题 * A:同一个数据出现多次 * B:姓名和年龄不匹配 * 原因: * A:同一个数据出现多次 * CPU的一点点时间片的执行权,就足够你执行很多次。 * B:姓名和年龄不匹配 * 线程运行的随机性 * 线程安全问题: * A:是否是多线程环境 是 * B:是否有共享数据 是 * C:是否有多条语句操作共享数据 是 * 解决方案: * 加锁。 * 注意: * A:不同种类的线程都要加锁。 * B:不同种类的线程加的锁必须是同一把。 * * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。 * 如何实现呢? * 通过Java提供的等待唤醒机制解决。 * * 等待唤醒: * Object类中提供了三个方法: * wait():等待 * notify():唤醒单个线程 * notifyAll():唤醒所有线程 * 为什么这些方法不定义在Thread类中呢? * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。 * 所以,这些方法必须定义在Object类中。 * * 最终版代码中: * 把Student的成员变量给私有的了。 * 把设置和获取的操作给封装成了功能,并加了同步。 * 设置或者获取的线程里面只需要调用方法即可。 */ 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(); } } |
(4)线程组
具体案例介绍
1. MyRunnable(线程类)
/* * 实现Runnable接口的线程类 */ public class MyRunnable implements Runnable {
@Override public void run() { for (int x = 0; x < 100; x++) { //打印当前线程的名称及数字 System.out.println(Thread.currentThread().getName() + ":" + x); } }
} |
2. ThreadGroupDemo(测试类)
/* * 线程组: 把多个线程组合到一起。 * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 * * 线程组类:ThreadGroup * 构造方法:public ThreadGroup(String name):构造一个带名称的线程组 * 普通方法:public final String getName():返回此线程组的名称。 * 注意:默认情况下,所有的线程都属于同一个组,就是main组 * Thread类: * 构造方法: * public Thread(ThreadGroup group, Runnable target, String name):创建一个属于某个线程组的且带名称的线程 普通方法 public final ThreadGroup getThreadGroup():返回该线程所属的线程组
*/ public class ThreadGroupDemo { public static void main(String[] args) { // method1();
// 我们如何修改线程所在的组呢? // 创建一个线程组 // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组 method2();
// t1.start(); // t2.start(); }
private static void method2() { // ThreadGroup(String name) ThreadGroup tg = new ThreadGroup("这是一个新的组");
MyRunnable my = new MyRunnable(); // Thread(ThreadGroup group, Runnable target, String name) Thread t1 = new Thread(tg, my, "林青霞"); Thread t2 = new Thread(tg, my, "刘意");
System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName());
//通过组名称设置后台线程,表示该组的线程都是后台线程 tg.setDaemon(true); }
private static void method1() { MyRunnable my = new MyRunnable(); Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "刘意"); // 我不知道他们属于那个线程组,我想知道,怎么办 // 线程类里面的方法:public final ThreadGroup getThreadGroup() ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); // 线程组里面的方法:public final String getName() String name1 = tg1.getName(); String name2 = tg2.getName(); System.out.println(name1); System.out.println(name2); // 通过结果我们知道了:线程默认情况下属于main线程组 // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组 System.out.println(Thread.currentThread().getThreadGroup().getName()); } } |
(5)线程池
案例代码体现:
1. MyRunnable(线程类)
/* * 实现Runnable接口的线程类 */ public class MyRunnable implements Runnable {
@Override public void run() { for (int x = 0; x < 100; x++) { //打印当前线程的名称及数字 System.out.println(Thread.currentThread().getName() + ":" + x); } }
} |
2. ExecutorsDemo(线程池类)
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
/* * 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 * * 如何实现线程的代码呢? * A:创建一个线程池对象,控制要创建几个线程对象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:这种线程池的线程可以执行: * 可以执行Runnable对象或者Callable对象代表的线程 * 做一个类实现Runnable接口。 * C:调用如下方法即可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要结束,可以吗? * 可以。 */ public class ExecutorsDemo { public static void main(String[] args) { // 创建一个线程池对象,控制要创建几个线程对象。 // public static ExecutorService newFixedThreadPool(int nThreads) ExecutorService pool = Executors.newFixedThreadPool(2);
// 可以执行Runnable对象或者Callable对象代表的线程 pool.submit(new MyRunnable()); pool.submit(new MyRunnable());
//结束线程池 pool.shutdown(); } } |
(6)多线程实现的第三种方案
多线程实现第三种方式介绍:
1. MyCallable
import java.util.concurrent.Callable;
//Callable:是带泛型的接口。 //这里指定的泛型其实是call()方法的返回值类型。 public class MyCallable implements Callable {
@Override public Object call() throws Exception { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } return null; }
} |
2. CallableDemo
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
/* : * A:创建一个线程池对象,控制要创建几个线程对象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:这种线程池的线程可以执行: * 可以执行Runnable对象或者Callable对象代表的线程 * 做一个类实现Runnable接口。 * C:调用如下方法即可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要结束,可以吗? * 可以。 */ public class CallableDemo { public static void main(String[] args) { //创建线程池对象 ExecutorService pool = Executors.newFixedThreadPool(2);
//可以执行Runnable对象或者Callable对象代表的线程 pool.submit(new MyCallable()); pool.submit(new MyCallable());
//结束 pool.shutdown(); } } |
多线程案例:求和
1. MyCallable
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; }
} |
2. CallableDemo
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;
/* : * A:创建一个线程池对象,控制要创建几个线程对象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:这种线程池的线程可以执行: * 可以执行Runnable对象或者Callable对象代表的线程 * 做一个类实现Runnable接口。 * C:调用如下方法即可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要结束,可以吗? * 可以。 */ 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(); } } |
(7)匿名内部类方法实现多线程
/* * 匿名内部类的格式: * new 类名或者接口名() { * 重写方法; * }; * 本质:是该类或者接口的子类对象。 */ public class ThreadDemo { public static void main(String[] args) { // 继承Thread类来实现多线程 new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }.start();
// 实现Runnable接口来实现多线程 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } } }) { }.start();
// 更有难度的,以下的这个代码是不会报错的,可以正常运行,需要能看懂 new Thread(new Runnable() { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println("hello" + ":" + x); } } }) { public void run() { for (int x = 0; x < 100; x++) { System.out.println("world" + ":" + x); } } }.start(); } } |
(8)定时器的介绍
1. TimerDemo类(任务完成之后,可以终止定时器)
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, Date time) * :安排在指定的时间执行指定的任务
* public void schedule(TimerTask task,long delay,long period) * :安排指定的任务在指定的时间开始进行重复的固定延迟执行。 * public void cancel():终止此计时器,丢弃所有当前已安排的任务。 * 这不会干扰当前正在执行的任务(如果存在)。 * TimerTask类:任务类 * 子类继承该方法,并重写里面的run()方法 */ 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(); } } |
2. TimerDemo2类(任务完成之后,不终止定时器)
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 TimerDemo2 { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); 秒再继续炸 t.schedule(new MyTask2(), 3000, 2000); //该程序不会自动停止 } }
// 做一个任务 class MyTask2 extends TimerTask { @Override public void run() { System.out.println("beng,爆炸了"); } } |
3. 案例:定时删除某个目录下的所有文件
import java.io.File; 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对象的数组 File[] fileArray = srcFolder.listFiles();
//判断该File数组是否为null if (fileArray != null) { //不为null,循环遍历 该数组 for (File file : fileArray) {
//判断该file对象是否为目录 if (file.isDirectory()) { //如果为目录,则进行递归 deleteFolder(file); } else { //不为目录,则打印被删除文件的名称并同时删除文件 System.out.println(file.getName() + ":" + file.delete()); } } //如果File数组为null,则打印被删除文件的名称并同时删除文件 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); } } |
(9)多线程的面试题
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:线程的生命周期图 新建 -- 就绪 -- 运行 -- 死亡 新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡 建议:画图解释。 |
2:设计模式(理解)
(1)面试对象的常见设计原则
单一
开闭
里氏
依赖注入
接口
迪米特
(2)设计模式概述和分类
A:经验的总结
B:三类
创建型
结构型
行为型
(3)改进的设计模式
A:简单工厂模式
1. Animal类(顶层抽象类)
/* * 动物类:抽象类 */ public abstract class Animal { //吃的方法 public abstract void eat(); } |
2. Cat类
/* * 猫类:继承动物类 */ public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); }
} |
3. Dog类
/* * 狗类:继承动物类 */ public class Dog extends Animal {
@Override public void eat() { System.out.println("狗吃肉"); } } |
4. AnimalFactory类(动物工厂类)
/* * 动物工厂类:专门造动物的 */ public class AnimalFactory { //构造方法私有,不允许创建对象 private AnimalFactory() { } //根据传递过来的类型,创建指定的对象,可通过类名直接调用 public static Animal createAnimal(String type) { if ("dog".equals(type)) { return new Dog(); } else if ("cat".equals(type)) { return new Cat(); } else { return null; } } } |
5. AnimalDemo(测试类)
/* * 测试类 */ public class AnimalDemo { public static void main(String[] args) { // 具体类调用 Dog d = new Dog(); d.eat(); Cat c = new Cat(); c.eat(); System.out.println("------------");
// 工厂改进后 Animal a = AnimalFactory.createAnimal("dog"); a.eat(); a = AnimalFactory.createAnimal("cat"); a.eat();
// NullPointerException,在使用对象之前,应该先对对象是否为null进行判断 a = AnimalFactory.createAnimal("pig"); if (a != null) { a.eat(); } else { System.out.println("对不起,暂时不提供这种动物"); } } } |
B:工厂方法模式
1. Animal(动物类)
/* * 动物类:抽象 类 */ public abstract class Animal { public abstract void eat(); } |
2. Cat类(猫类)
/* * 猫类:继承自动物类 */ public class Cat extends Animal {
@Override public void eat() { System.out.println("猫吃鱼"); }
} |
3. Dog(狗类)
/* * 狗类:继承自动物类 */ public class Dog extends Animal {
@Override public void eat() { System.out.println("狗吃肉"); }
} |
4. Factory(接口)
/* * Factory:工厂接口 */ public interface Factory { //定义了创建动物的方法 public abstract Animal createAnimal(); } |
5. CatFactory(猫工厂)
/* * CatFactory:猫工厂,实现工厂接口,专门用来生成猫 */ public class CatFactory implements Factory {
@Override public Animal createAnimal() { return new Cat(); }
} |
6. DogFactory(狗工厂)
/* * 狗工厂:实现了工厂接口,专门用来造狗 */ public class DogFactory implements Factory {
@Override public Animal createAnimal() { return new Dog(); }
} |
7. AnimalDemo测试类
/* * 测试类 */ public class AnimalDemo { public static void main(String[] args) { // 需求:我要买只狗 Factory f = new DogFactory(); Animal a = f.createAnimal(); a.eat(); System.out.println("-------");
//需求:我要买只猫 f = new CatFactory(); //使用特定的工厂专门造狗,造猫 a = f.createAnimal(); a.eat(); } } |
C:单例模式(掌握)
a:饿汉式:
说明:饿汉式:提前造好一个对象(开发中常用,因为不容易出现问题)
1.
/* * 单例模式:饿汉式:提前造好一个对象 */ public class Student { // 构造私有 private Student() { }
// 自己造一个 // 静态方法只能访问静态成员变量,加静态 // 为了不让外界直接访问修改这个值,加private private static Student s = new Student();
// 提供公共的访问方式 // 为了保证外界能够直接使用该方法,加静态 public static Student getStudent() { return s; } } |
2. StudentDemo测试类
/* * 单例模式:保证类在内存中只有一个对象。 * * 如何保证类在内存中只有一个对象呢? * A:把构造方法私有 * B:在成员位置自己创建一个对象 * C:通过一个公共的方法提供访问 */ public class StudentDemo { public static void main(String[] args) { //通过单例模式得到对象 Student s1 = Student.getStudent(); Student s2 = Student.getStudent(); System.out.println(s1 == s2);
System.out.println(s1); // null,cn.itcast_03.Student@175078b System.out.println(s2);// null,cn.itcast_03.Student@175078b } } |
b:懒汉式:
说明:需要用的时候,才去创建对象。(面试常面)
1. Teacher类
/* * 单例模式: * 饿汉式:类一加载就创建对象 * 懒汉式:用的时候,才去创建对象 * * 面试题:单例模式的思想是什么?请写一个代码体现。 * * 开发:饿汉式(是不会出问题的单例模式) * 面试:懒汉式(可能会出问题的单例模式) * A:懒加载(延迟加载) * B:线程安全问题 * a:是否多线程环境 是 * b:是否有共享数据 是 * c:是否有多条语句操作共享数据 是 */ public class Teacher { //构造方法私有化 private Teacher() { }
//定义一个对象 private static Teacher t = null;
//加入同步关键字解决线程安全问题 public synchronized static Teacher getTeacher() { // t1,t2,t3 //如果对象为null,就创建,否则不创建 if (t == null) { //t1,t2,t3 t = new Teacher(); } return t; } } |
2. TeacherDemo类
/* * 测试类 */ public class TeacherDemo { public static void main(String[] args) { Teacher t1 = Teacher.getTeacher(); Teacher t2 = Teacher.getTeacher(); System.out.println(t1 == t2); System.out.println(t1); // cn.itcast_03.Teacher@175078b System.out.println(t2);// cn.itcast_03.Teacher@175078b } } |
(4)Runtime
JDK提供的一个单例模式应用的类。
还可以调用dos命令。
import java.io.IOException;
/* * Runtime:每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。 * exec(String command) */ public class RuntimeDemo { public static void main(String[] args) throws IOException { Runtime r = Runtime.getRuntime(); // r.exec("winmine"); //打开记事本 // r.exec("notepad"); //打开计算器 // r.exec("calc"); //关机(时间是以秒计算的) // r.exec("shutdown -s -t 10000"); //取消shutdown的命令 r.exec("shutdown -a"); } }
/*该Runtime类使用了单例模式 * class Runtime { * private Runtime() {} * private static Runtime currentRuntime = new Runtime(); * public static Runtime getRuntime() { * return currentRuntime; * } * } */ |
javaSE第二十四天的更多相关文章
- NeHe OpenGL教程 第二十四课:扩展
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- Python第二十四天 binascii模块
Python第二十四天 binascii模块 binascii用来进行进制和字符串之间的转换 import binascii s = 'abcde' h = binascii.b2a_hex(s) # ...
- Gradle 1.12用户指南翻译——第二十四章. Groovy 插件
其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...
- SQL注入之Sqli-labs系列第二十四关(二阶注入)
开始挑战第二十四关(Second Degree Injections) 0x1 前言 SQL注入一般分为两类:一阶SQL注入(普通SQL注入),二阶SQL注入 .二次注入不是注入两次的意思,请不要混淆 ...
- “全栈2019”Java多线程第二十四章:等待唤醒机制详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java第二十四章:流程控制语句中决策语句switch下篇
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 孤荷凌寒自学python第二十四天python类中隐藏的私有方法探秘
孤荷凌寒自学python第二十四天python类中隐藏的私有方法探秘 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天发现了python的类中隐藏着一些特殊的私有方法. 这些私有方法不管我 ...
- 第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题
第二十四届全国青少年信息学奥林匹克联赛初赛 普及组C++语言试题 1.原题呈现 2.试题答案 3.题目解析 因博客园无法打出公式等,所以给你们几个小编推荐的链接去看看,在这里小编深感抱歉! https ...
- 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击
风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...
随机推荐
- JAVA用户数据输入
数据输入 首先需要导入扫描仪 然后声明扫描仪 输出输入提示 接收用户数据的数据 输出用户数据的数据 实例: import java.util.Scanner; //导入扫描仪 public class ...
- js鼠标事件、键盘事件实例代码
讲述了:鼠标的哪个按键被点击.当前鼠标的光标坐标是多少.被按下键的unicode码是多少.当前鼠标的光标相对于屏幕的坐标是多少.当前鼠标的光标坐标是多少.shift键是否按下.当前被点击的是哪一个元素 ...
- git添加标签(转载)
From:http://git-scm.com/book/zh/v1/Git-%E5%9F%BA%E7%A1%80-%E6%89%93%E6%A0%87%E7%AD%BE 打标签 同大多数 VCS 一 ...
- java中读取文件以及向文件中追加数据的总结
package gys; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; imp ...
- Mysql 学习笔记 20140219
1. Mysql常用命令:每个命令以分号结束. create database name; 创建数据库 use databasename; 选择数据库 dr ...
- poj 1094 Sorting It All Out(nyoj 349)
点击打开链接 Sorting It All Out Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 24544 Accep ...
- NYOJ-228 士兵杀敌5
士兵杀敌(五) 时间限制:2000 ms | 内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为0~M,每次有任务的时候,总会有一批编号连在一起人请战(编 ...
- (转)C# Color类图示
本文来源 http://www.cnblogs.com/lv8218218/archive/2010/12/20/1911746.html
- 分布式消息队列的使用kakfa
作用: 1.系统解耦 2.通过消息可以减少系统的处理压力,做一个缓冲 3.原理基于生存者消费者的模式 4.两种场景:队列(端对端 一对一).主题(一对多,广播模式) 5.消息:header.body构 ...
- 10G之后统计信息收集后为什么执行计划不会被立马淘汰
在10G之前,使用DBMS_STATS收集统计信息将会导致与此对象相关的游标失效,下次执行此 的时候将会进行HARD PARSE,除非收集的时候NO_INVALIDATE设置为TRUE. 由于硬解析会 ...