今天,咱们就把线程给完完全全的结束掉,但是不是说,就已经覆盖了全部的知识点,可以说是线程的常见的问题及所含知识基本都包含。
1、多线程(理解)
(1)JDK5以后的针对线程的锁定操作和释放操作
Lock锁
(2)死锁问题的描述和代码体现
同步的弊端:
A:效率低
B:容易产生死锁
死锁:
两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待现象。
举例:
中国人和外国人吃饭的案例
正常情况下:
中国人:筷子两支
外国人:刀和叉
死锁情况下:
中国人:筷子1支,刀一把。
外国人:筷子1支,叉一把。
(都在等待对方给自己的用具)
用代码怎么体现一个死锁问题?(面试题)
创建一个锁类,有两把锁:
1 package com.wyh.lock; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午8:27:03 6 */ 7 public class MyLock { 8 9 //定义两把锁 10 public static final Object objA = new Object(); 11 public static final Object objB = new Object(); 12 13 }
创建线程类,重写run方法:
1 package com.wyh.lock; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午8:28:17 6 */ 7 public class DieLock extends Thread{ 8 private boolean flag; 9 10 11 public DieLock(boolean flag) { 12 this.flag = flag; 13 } 14 15 @Override 16 public void run() { 17 if(flag) { 18 synchronized(MyLock.objA) { 19 System.out.println("if objA");//情况是当dl1走到这里等待锁B 20 synchronized(MyLock.objB) { 21 System.out.println("if objB"); 22 } 23 } 24 }else { 25 synchronized(MyLock.objB) { 26 System.out.println("else objB");//当dl2走到这里等待锁A 27 synchronized(MyLock.objA) { 28 System.out.println("else objA"); 29 } 30 } 31 } 32 } 33 34 }
编写测试类:
1 package com.wyh.lock; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午8:33:50 6 */ 7 public class DieLockDemo { 8 public static void main(String[] args) { 9 DieLock dl1 = new DieLock(true); 10 DieLock dl2 = new DieLock(false); 11 12 dl1.start(); 13 dl2.start(); 14 } 15 16 }
(3)生产者和消费者多线程体现(线程间的通信问题)
上一节我们写的售票程序并不符合实际情况。
以学生作为资源来实现的
资源类:student
设置数据类:SetThread(生产者)
获取数据类:GetThread(消费者)
测试类:StudentDemo
代码:
A:最基本的版本,只有一个数据。
Student类:
1 package 生产者消费者01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:02 6 */ 7 public class Student { 8 String name; 9 int age; 10 11 }
SetThread类:(为了保证多个线程是操作同一个学生,我们重写有参构造方法)
1 package 生产者消费者01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:35 6 */ 7 public class StudentSetThread implements Runnable { 8 private Student s ; 9 10 public StudentSetThread(Student s) { 11 this.s = s; 12 } 13 14 @Override 15 public void run() { 16 s.name = "王友虎"; 17 s.age = 22; 18 } 19 20 }
SetThread类:
1 package 生产者消费者01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:04:46 6 */ 7 public class StudentGetThread implements Runnable { 8 private Student s ; 9 10 public StudentGetThread(Student s) { 11 this.s = s; 12 } 13 14 @Override 15 public void run() { 16 System.out.println(s.name+"---"+s.age); 17 } 18 19 }
测试类;
1 package 生产者消费者01; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:05:35 6 */ 7 public class StudentThreadDemo { 8 public static void main(String[] args) { 9 Student s = new Student(); 10 StudentSetThread st = new StudentSetThread(s); 11 StudentGetThread sg = new StudentGetThread(s); 12 13 Thread t1 = new Thread(st); 14 Thread t2 = new Thread(sg); 15 16 t2.start(); 17 t1.start(); 18 19 } 20 }
但是运行多次我们发现,有出现姓名为null或者年龄为0的情况出现,这是为什么?
这是因为CPU进行的操作都是原子操作,有情况是当对年龄进行赋值,还没来得及对姓名赋值,下一个线程就进行输出。从而导致有null的情况,其他情况类推。
B:改进版本,给出了不同的数据。并加入了同步机制。
为了数据效果好一点,我们设置循环和判断,给出不同的值.
学生类:
1 package 生产者消费者02; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:02 6 */ 7 public class Student { 8 String name; 9 int age; 10 11 }
SetThread类:
1 package 生产者消费者02; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:35 6 */ 7 public class StudentSetThread implements Runnable { 8 private Student s ; 9 private int x = 0; 10 11 public StudentSetThread(Student s) { 12 this.s = s; 13 } 14 15 @Override 16 public void run() { 17 while(true) {19 if(x%2==0) { 20 s.name = "王友虎"; 21 s.age = 22; 22 }else { 23 s.name = "李智恩"; 24 s.age = 20; 25 }27 x++; 28 } 29 30 } 31 32 }
GetThread类:
1 package 生产者消费者02; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:04:46 6 */ 7 public class StudentGetThread implements Runnable { 8 private Student s ; 9 10 public StudentGetThread(Student s) { 11 this.s = s; 12 } 13 14 @Override 15 public void run() { 16 while(true) {18 System.out.println(s.name+"---"+s.age);20 } 21 22 } 23 24 }
测试类:
1 package 生产者消费者02; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:05:35 6 * 7 * 解决线程安全的问题 8 * 9 */ 10 public class StudentThreadDemo { 11 public static void main(String[] args) { 12 Student s = new Student(); 13 StudentSetThread st = new StudentSetThread(s); 14 StudentGetThread sg = new StudentGetThread(s); 15 16 Thread t1 = new Thread(st); 17 Thread t2 = new Thread(sg); 18 19 t2.start(); 20 t1.start(); 21 22 } 23 }
运行结果:
我们发现了问题
1:同一个数据出现多次
CPU的一点点时间片的执行权,就足够执行很多次
2:姓名和年龄不匹配
线程运行的随机性
很显然,我们这个改进还是存在线程安全的问题:
怎么判断一个线程是不是安全的:
1:是否是多线程环境 是
2:是都有共享数据 是
3:是否有多条语句操作共享数据 是
C:等待唤醒机制改进程序。让数据能够实现依次的出现
wait() :等待
notify(): 唤醒单个线程
notifyAll(): 唤醒多个线程
(上面这3个方法的调用必须是通过锁对象调用,而当我们使用的锁悐是任意锁,所以和没加锁区别不大,所以,这些方法必须定义在Object类中s)
对上面的进行解决:加锁,synchronized,并且通过等待唤醒机制进行操作。保证数据是依次出现的
注意:1、不同种类的线程都要加锁。 2、不同种类的线程加的锁必须是同一把。
学生类:
1 package 生产者消费者03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:02 6 */ 7 public class Student { 8 String name; 9 int age; 10 boolean flag; //默认没有数据.false 11 }
SetThread类:
1 package 生产者消费者03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:35 6 */ 7 public class StudentSetThread implements Runnable { 8 private Student s ; 9 private int x = 0; 10 11 public StudentSetThread(Student s) { 12 this.s = s; 13 } 14 15 @Override 16 public void run() { 17 while(true) { 18 synchronized(s) { 19 if(s.flag) { 20 try { 21 s.wait(); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 27 if(x%2==0) { 28 s.name = "王友虎"; 29 s.age = 22; 30 }else { 31 s.name = "李智恩"; 32 s.age = 20; 33 } 34 35 //此时有数据 36 s.flag = true; 37 //唤醒线程 38 s.notify(); //唤醒不代表立即执行,还需要争夺CPU执行权. 39 40 } 41 x++; 42 43 44 45 } 46 47 } 48 49 }
GetThread类:
1 package 生产者消费者03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:04:46 6 */ 7 public class StudentGetThread implements Runnable { 8 private Student s ; 9 10 public StudentGetThread(Student s) { 11 this.s = s; 12 } 13 14 @Override 15 public void run() { 16 while(true) { 17 synchronized (s) { 18 if(!s.flag) { 19 try { 20 s.wait(); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 System.out.println(s.name+"---"+s.age); 26 27 //消費了 28 s.flag = false; 29 //綫程 30 s.notify(); 31 32 } 33 } 34 35 } 36 37 }
测试类:
1 package 生产者消费者03; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:05:35 6 * 7 * 等待唤醒机制 8 * 9 */ 10 public class StudentThreadDemo { 11 public static void main(String[] args) { 12 Student s = new Student(); 13 StudentSetThread st = new StudentSetThread(s); 14 StudentGetThread sg = new StudentGetThread(s); 15 16 Thread t1 = new Thread(st); 17 Thread t2 = new Thread(sg); 18 19 t2.start(); 20 t1.start(); 21 22 } 23 }
D:资源唤醒机制的代码优化,
把数据及操作都写在了资源类中,同步方法实现。
学生类:
1 package 生产者消费者04优化; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:02 6 * 7 * 8 * 用同步方法来优化 9 * 10 */ 11 public class Student { 12 private String name; 13 private int age; 14 private boolean flag; //默认没有数据.false 15 16 17 public synchronized void set(String name,int age) { 18 if(flag) { 19 try { 20 this.wait(); 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 26 this.name = name; 27 this.age = age; 28 29 //修改 30 flag = true; 31 32 //唤醒 33 this.notify(); 34 } 35 36 37 public synchronized void get() { 38 if(!flag) { 39 try { 40 this.wait(); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 46 System.out.println(this.name + "---" + this.age); 47 48 //修改 49 flag = false; 50 51 //唤醒 52 this.notify(); 53 } 54 55 56 57 }
SetThread类:
1 package 生产者消费者04优化; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:03:35 6 */ 7 public class StudentSetThread implements Runnable { 8 private Student s ; 9 private int x = 0; 10 11 public StudentSetThread(Student s) { 12 this.s = s; 13 } 14 15 @Override 16 public void run() { 17 while(true) { 18 if(x%2==0) { 19 s.set("王友虎", 22); 20 }else { 21 s.set("李智恩", 20); 22 } 23 x++; 24 } 25 } 26 27 }
GetThread类:
1 package 生产者消费者04优化; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:04:46 6 */ 7 public class StudentGetThread implements Runnable { 8 private Student s ; 9 10 public StudentGetThread(Student s) { 11 this.s = s; 12 } 13 14 @Override 15 public void run() { 16 while(true) { 17 s.get(); 18 19 } 20 } 21 22 }
测试类:
1 package 生产者消费者04优化; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午9:05:35 6 * 7 * 等待唤醒机制 8 * 9 */ 10 public class StudentThreadDemo { 11 public static void main(String[] args) { 12 Student s = new Student(); 13 StudentSetThread st = new StudentSetThread(s); 14 StudentGetThread sg = new StudentGetThread(s); 15 16 Thread t1 = new Thread(st); 17 Thread t2 = new Thread(sg); 18 19 t2.start(); 20 t1.start(); 21 22 } 23 }
线程的状态转换图:
(4)线程组(用实现Runnable接口的方式举例)
1 package 线程组1; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午10:30:51 6 */ 7 public class MyRunnable implements Runnable{ 8 9 @Override 10 public void run() { 11 System.out.println(Thread.currentThread().getName()); 12 13 14 } 15 16 }
测试类:
1 package 线程组1; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午10:31:37 6 */ 7 public class MyRunnableDemo { 8 public static void main(String[] args) { 9 ThreadGroup tg = new ThreadGroup("这是一个新的线程组"); 10 11 MyRunnable mr = new MyRunnable(); 12 13 Thread t1 = new Thread(tg,mr,"王友虎"); 14 Thread t2 = new Thread(tg,mr,"李智恩"); 15 16 17 System.out.println(t1.getThreadGroup().getName()); 18 System.out.println(tg.getName()); 19 } 20 21 }
(5)线程池
程序启动一个新的线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
1、线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
2、在JDK5之前,我们必须手动实现自己的线程池,从JDk5开始,Java内置支持线程池。
如何实现线程池的代码呢?
1、创建一个线程对象,控制要创建几个线程对象。
public static ExecutorService newFixedThreadPool(int nThread)
2、这种线程池的线程可以执行:
可以执行Runnable对象或者Callable对象代表的线程
做一个类实现Runnable接口。
3、调用下面的方法即可
Future submit(Runnable task)
<T> future<T> submit(Callable<T> task)
4、我就要结束,可以吗? 可以。
shutdown()
实现Runnable接口:
1 package 线程池; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 上午11:08:38 6 */ 7 public class MyRunnable implements Runnable{ 8 9 @Override 10 public void run() { 11 for(int x = 0;x<500;x++) { 12 System.out.println(Thread.currentThread().getName()+":"+x); 13 } 14 15 } 16 17 }
测试类:
1 package 线程池; 2 3 import java.util.concurrent.Executor; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 7 /** 8 * @author WYH 9 * @version 2019年11月23日 上午11:09:57 10 * 11 * 线程池的创建 12 */ 13 public class ExecutorsDemo { 14 public static void main(String[] args) { 15 //创建线程池 16 ExecutorService pool = Executors.newFixedThreadPool(2); 17 ExecutorService pool1 = Executors.newCachedThreadPool(); 18 19 pool.submit(new MyRunnable()); 20 pool.submit(new MyRunnable()); 21 22 pool1.submit(new MyRunnable()); 23 pool1.submit(new MyRunnable()); 24 pool1.submit(new MyRunnable()); 25 26 pool.shutdown(); 27 pool1.shutdown(); 28 29 } 30 31 }
(6)多线程实现的第三种方案
MyCallable类:
1 package com.wyh.callable; 2 3 import java.util.concurrent.Callable; 4 5 /** 6 * @author WYH 7 * @version 2019年11月23日 下午2:29:25 8 */ 9 public class MyCallable implements Callable { 10 11 @Override 12 public Object call() throws Exception { 13 for(int x = 0;x<500;x++) { 14 System.out.println(Thread.currentThread().getName()+":"+x); 15 } 16 return null; 17 } 18 19 }
测试类:
1 package com.wyh.callable; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 6 /** 7 * @author WYH 8 * @version 2019年11月23日 下午2:30:21 9 * 10 * 创建线程的第三种方式 11 */ 12 public class MyCallableDemo { 13 public static void main(String[] args) { 14 //创建线程池 15 ExecutorService pool = Executors.newFixedThreadPool(2); 16 17 pool.submit(new MyCallable()); 18 pool.submit(new MyCallable()); 19 20 pool.shutdown(); 21 } 22 23 }
(6_2)Callable的案例(计算1+....的总和 线程池实现)
Runnable类:
1 package com.wyh.callable案例1; 2 3 import java.util.concurrent.Callable; 4 5 /** 6 * @author WYH 7 * @version 2019年11月23日 下午2:29:25 8 */ 9 public class MyCallable implements Callable<Integer> { 10 private int number; 11 12 public MyCallable(int number) { 13 this.number = number; 14 } 15 16 17 @Override 18 public Integer call() throws Exception { 19 int sum =0; 20 for(int x = 1;x<=number;x++) { 21 sum += x; 22 // System.out.println(Thread.currentThread().getName()+":"+sum); 23 } 24 // System.out.println(Thread.currentThread().getName()+":"+sum); 25 return sum; 26 } 27 28 }
测试类:
1 package com.wyh.callable案例1; 2 3 import java.beans.FeatureDescriptor; 4 import java.util.concurrent.ExecutionException; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.Future; 8 9 /** 10 * @author WYH 11 * @version 2019年11月23日 下午2:30:21 12 * 13 * 创建线程的第三种方式 14 */ 15 public class MyCallableDemo { 16 public static void main(String[] args) throws InterruptedException, ExecutionException { 17 //创建线程池 18 ExecutorService pool = Executors.newFixedThreadPool(3); 19 20 Future<Integer> f1 = pool.submit(new MyCallable(50)); 21 Future<Integer> f2 = pool.submit(new MyCallable(100)); 22 Future<Integer> f3 = pool.submit(new MyCallable(200)); 23 24 int i1 = f1.get(); 25 int i2 = f2.get(); 26 int i3 = f3.get(); 27 28 29 System.out.println(i1); 30 System.out.println(i2); 31 System.out.println(i3); 32 33 34 pool.shutdown(); 35 } 36 37 }
(7)多线程的面试题
a:多线程有几种实现方案,分别是哪几种?
两种。(面试答2种)
继承Thread类
实现Runnable接口
扩展一种,实现Callable接口,这个要和线程池结合使用。
b:同步有几种方式,分别是是什么?
两种。
同步代码块 锁是任意对象锁
同步方法 锁是this
同步静态方法 锁是当前类的二进制字节码文件
c:启动一个线程是run()还是start()?它们的区别?
start()
run():封装了被线程执行的代码块,直接调用仅仅是一个普通方法的调用
start():启动线程,并由JVM自动调用run()方法
d:sleep()和wait()方法的区别
sleep():必须指定时间,不释放锁。
wait():可以不指定时间,也可以指定时间,但是它释放锁。
e:为什么wait(), notify(), notifyAll()等方法都定义在Object类中?
因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。而Object代表任意对象,所以,定义在里面。
f:线程的生命周期图(如上)
新建 -- 就绪 -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡
2、设计模式(理解)
(1)面试对象的常见设计原则
单一职责原则
开闭原则
里氏替换原则
依赖注入原则
接口分离原则
迪米特原则
(2)设计模式概述和分类
A:经验的总结
B:三类:
创建型
结构型
行为型
(3)改进的设计模式
A:简单工厂模式
Animal类:
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:36:06 6 */ 7 public abstract class Animal { 8 public abstract void eat(); 9 10 }Animal
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:37:44 6 */ 7 public class AnimalFactory { 8 private AnimalFactory(){ 9 10 } 11 12 public static Animal CreateAnimal(String type) { 13 if(type.equals("dog")) { 14 return new Dog(); 15 }else if(type.equals("cat")) { 16 return new Cat(); 17 }else { 18 return null; 19 } 20 } 21 22 }AnimalFactory
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:37:16 6 */ 7 public class Cat extends Animal { 8 9 @Override 10 public void eat() { 11 System.out.println("猫吃鱼"); 12 13 } 14 15 }Cat
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:36:45 6 */ 7 public class Dog extends Animal { 8 9 @Override 10 public void eat() { 11 System.out.println("狗吃肉"); 12 13 14 } 15 16 }Dog
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:41:54 6 */ 7 public class FactoryDemo { 8 public static void main(String[] args) { 9 Animal a = AnimalFactory.CreateAnimal("dog"); 10 a.eat(); 11 Animal b = AnimalFactory.CreateAnimal("cat"); 12 b.eat(); 13 14 Animal c = AnimalFactory.CreateAnimal("pig"); 15 if(c != null) { 16 c.eat(); 17 }else { 18 System.out.println("对不起,该工厂无法造该对象."); 19 } 20 21 22 23 } 24 25 }FactoryDemo
B:工厂方法模式
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:18:40 6 */ 7 public abstract class Animal { 8 public abstract void eat(); 9 10 }Animal
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:19:23 6 */ 7 public interface Factory { 8 public abstract Animal CreateAnimal(); 9 10 }Factory
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:20:39 6 */ 7 public class Dog extends Animal { 8 9 @Override 10 public void eat() { 11 System.out.println("狗吃肉"); 12 13 } 14 15 }Dog
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:22:01 6 */ 7 public class DogFactory implements Factory { 8 9 @Override 10 public Animal CreateAnimal() { 11 return new Dog(); 12 } 13 14 }DogFactory
1 package 工厂设计模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午4:37:16 6 */ 7 public class Cat extends Animal { 8 9 @Override 10 public void eat() { 11 System.out.println("猫吃鱼"); 12 13 } 14 15 }Cat
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:23:46 6 */ 7 public class CatFactory implements Factory { 8 9 @Override 10 public Animal CreateAnimal() { 11 return new Cat(); 12 } 13 14 }CatFactory
1 package 工厂方法模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午5:20:03 6 */ 7 public class FactoryDemo { 8 public static void main(String[] args) { 9 //我需要狗 10 Factory f = new DogFactory(); 11 Animal a = f.CreateAnimal(); 12 a.eat(); 13 14 //我需要猫 15 f = new CatFactory(); 16 Animal a1 = f.CreateAnimal(); 17 a1.eat(); 18 19 } 20 21 }FactoryDemo
C:单例模式(掌握)
a:饿汉式
Student类:
1 package 单例模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午6:20:40 6 */ 7 public class Student { 8 // 将构造方法私有 9 private Student() { 10 11 } 12 13 // 自己创建一个对象 14 // 静态方法只能访问静态的成员变量,加static 15 // 为了不让外界随意改动这个对象,我们将他私有化 16 private static Student s = new Student(); 17 18 // 提供一个方法给外界进行访问到这个对象 19 public static Student getStudent() { 20 return s; 21 } 22 23 }
测试类:
1 package 单例模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午6:20:47 6 * 7 * 单例模式:保证类在内存中指存在一个对象 8 * 饿汉式 9 * 10 */ 11 public class StudentDemo1 { 12 public static void main(String[] args) { 13 14 Student s1 = Student.getStudent(); 15 Student s2 = Student.getStudent(); 16 17 System.out.println(s1 == s2); 18 } 19 20 }
b:懒汉式
Teacher类:
1 package 单例模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午6:32:09 6 * 7 * 懒汉式 8 */ 9 public class Teacher { 10 private Teacher() { 11 12 } 13 14 private static Teacher teacher = null; 15 16 public synchronized static Teacher getTeacher() { 17 if(teacher == null) { 18 teacher = new Teacher(); 19 } 20 21 return teacher; 22 } 23 24 }
测试类:
1 package 单例模式; 2 3 /** 4 * @author WYH 5 * @version 2019年11月23日 下午6:34:12 6 */ 7 public class TeacherDemo { 8 public static void main(String[] args) { 9 Teacher t1 = Teacher.getTeacher(); 10 Teacher t2 = Teacher.getTeacher(); 11 12 System.out.println(t1 == t2); 13 System.out.println(t1); 14 System.out.println(t2); 15 16 17 } 18 19 }
(注意:面试的时候写懒汉式,开发的时候写饿汉式,因为饿汉式不存在线程安全问题)
(4)Runtime
JDK提供的一个单例模式应用的类
还可以通过dos命令。
例子:
1 package 单例模式; 2 3 import java.io.IOException; 4 5 /** 6 * @author WYH 7 * @version 2019年11月23日 下午6:43:36 8 * 9 * 饿汉式的一个类的例子 10 * 11 * 12 */ 13 public class RunTimeDemo { 14 public static void main(String[] args) throws IOException { 15 Runtime r = Runtime.getRuntime(); 16 // r.exec("calc"); //调出计算器 17 // r.exec("shutdown -s -t 10000"); 18 r.exec("shutdown -a"); 19 } 20 } 21 22 /* 23 * 源码: 24 * 25 * class Runtime{ 26 * private Runtime() {} 27 * private static Runtime currentRuntime = new Runtime(); 28 * public static Runtime getRuntime() { 29 * return currentRuntime; 30 * } 31 * 32 * } 33 */