今天,咱们就把线程给完完全全的结束掉,但是不是说,就已经覆盖了全部的知识点,可以说是线程的常见的问题及所含知识基本都包含。
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 }

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 }

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 }

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 }

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 }
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 }

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 }

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 }

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 }

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 }

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 }

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 }
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 */
