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多线程的使用2
1.join与interrupt的用法 class Sleeper extends Thread { private int duration; public Sleeper(String name, ...
- git的忽略文件和删除文件操作
1 删除工作区和暂存去的a文件$ git rm a 2只删除暂存去的 a文件,a文件就不被跟踪了.可以执行git add a从新添加回暂存去$ git rm --cached a 3 git mv 操 ...
- [JavaScript] JS中对Base64的解析
JS中对Base64的解析 <script type="text/javascript"> /** * UTF16和UTF8转换对照表 * U+00000000 – U ...
- nyoj 95 众数问题
点击打开链接 众数问题 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 所谓众数,就是对于给定的含有N个元素的多重集合,每个元素在S中出现次数最多的成为该元素的重数, 多 ...
- POJ 3264-Balanced Lineup, NYOJ 119-士兵杀敌3 线段树
士兵杀敌(三) 时间限制:2000 ms | 内存限制:65535 KB 难度:5 描述 南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比 ...
- memcpy、memmove、memset
void * memcpy(void * dst, const void * src, size_t count) { void *res=dst; while (count--) { *(char* ...
- POJ 1542 Atlantis(线段树 面积 并)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1542 参考网址:http://blog.csdn.net/sunmenggmail/article/d ...
- How to Resize a Datafile (文档 ID 1029252.6)
APPLIES TO: Oracle Database - Enterprise Edition - Version 9.2.0.1 and laterInformation in this docu ...
- python函数 位置参数,关键字参数,可变参数优先级
def fun(arg,args=1,*arg,**keywords): python 一共有这四类参数,第一类最常见,不用多说,第二类,关键字参数,python能通过关键字找到参数,python函数 ...
- centos6.5网络配置问题:ping不通
遇到的问题:win7网卡ping不通 win7中的VMnet8信息 centos6.5设置: /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE=eth0 ...