单例模式-多线程环境
单例-立即加载:
1 /**
2 * 单例模式,立即加载
3 */
4 public class MyObject {
5
6 private static MyObject myObject = new MyObject();//立即加载(类加载的时候就已经实例化好了)
7
8 private MyObject() {}
9
10 public static MyObject getInstance() {
11 /**
12 * 此代码版本为立即加载
13 * 缺点是不能有其他实例变量,因为getInstance()方法没有同步,所有可能出现非线程安全问题
14 */
15 return myObject;
16 }
17 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类,创建三个线程分别去获取实例并输出hashCode值
3 * 通过结果可以看出三个线程打印的hashCode值相同,说明对象是同一个,并且实现了立即加载型单例设计模式
4 */
5 public class Run {
6
7 public static void main(String[] args) {
8 MyThread t1 = new MyThread();
9 MyThread t2 = new MyThread();
10 MyThread t3 = new MyThread();
11
12 t1.start();
13 t2.start();
14 t3.start();
15 }
16 }
单例-延迟加载:(该版本单例模式,如果在多线程环境,则可能会出现多个实例)
1 /**
2 * 单例设计类,延迟加载,当需要用再创建实例
3 */
4 public class MyObject {
5
6 private static MyObject myObject;
7
8 private MyObject() {}
9
10 public static MyObject getInstance() {
11 if(myObject != null) {//延迟加载,也就是当用到了再创建
12 }else {
13 myObject = new MyObject();
14 }
15 return myObject;
16 }
17 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 如果是在多线程环境,会出现多个实例的情况,此时就与单例模式的初衷相违背了
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 t1.start();
12 }
13 }
演示:延迟单例模式,出现多个实例
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 private static MyObject myObject;
7
8 private MyObject() {}
9
10 public static MyObject getInstance() {
11 try {
12 if(myObject != null) {
13 }else {
14 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15 myObject = new MyObject();
16 }
17 } catch (InterruptedException e) {
18 e.printStackTrace();
19 }
20 return myObject;
21 }
22 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 通过运行结果可以看到打印的三个线程获取的对象的hashCode值不同,也就是出现了多例
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 MyThread t2 = new MyThread();
12 MyThread t3 = new MyThread();
13 t1.start();
14 t2.start();
15 t3.start();
16 }
17 }
优化1:优化出现多例的情况,将整个方法加上同步锁
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 private static MyObject myObject;
7
8 private MyObject() {}
9
10 //防止多线程出现多例,此处将整个方法加上同步锁(效率低)
11 synchronized public static MyObject getInstance() {
12 try {
13 if(myObject != null) {
14 }else {
15 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
16 myObject = new MyObject();
17 }
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
21 return myObject;
22 }
23 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出是同步运行,但是整个方法加上同步锁,效率太低
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 MyThread t2 = new MyThread();
12 MyThread t3 = new MyThread();
13 t1.start();
14 t2.start();
15 t3.start();
16 }
17 }
再次优化:使用同步代码块
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 private static MyObject myObject;
7
8 private MyObject() {}
9
10 //防止多线程出现多例,在使用同步代码块
11 public static MyObject getInstance() {
12 try {
13 synchronized(MyObject.class) {
14 if(myObject != null) {
15 }else {
16 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
17 myObject = new MyObject();
18 }
19 }
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 return myObject;
24 }
25 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出是同步运行,修改成使用同步代码块,效率也是低因为整个方法的代码都在同步块中
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 MyThread t2 = new MyThread();
12 MyThread t3 = new MyThread();
13 t1.start();
14 t2.start();
15 t3.start();
16 }
17 }
继续优化:针对重要代码使用同步代码块
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 private static MyObject myObject;
7
8 private MyObject() {}
9
10 public static MyObject getInstance() {
11 try {
12 if(myObject != null) {
13 }else {
14 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15 //虽然部分代码上锁,但是还是存在非线程安全问题
16 synchronized(MyObject.class) {
17 myObject = new MyObject();
18 }
19 }
20 } catch (InterruptedException e) {
21 e.printStackTrace();
22 }
23 return myObject;
24 }
25 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出得到多个实例
8 * 该优化只对实例对象的关键代码进行同步,结构上来说效率提升了,但是遇到多线程,还是无法解决得到同一个实例对象
9 */
10 public static void main(String[] args) {
11 MyThread t1 = new MyThread();
12 MyThread t2 = new MyThread();
13 MyThread t3 = new MyThread();
14 t1.start();
15 t2.start();
16 t3.start();
17 }
18 }
终极优化:使用DCL双检查锁机制实现多线程中延迟加载单例设计模式,解决出现多例的情况以及效率问题
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5 //使用volatile关键字
6 private volatile static MyObject myObject;
7
8 private MyObject() {}
9
10 public static MyObject getInstance() {
11 try {
12 if(myObject != null) {
13 }else {
14 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15 synchronized(MyObject.class) {
16 if(myObject == null) {
17 myObject = new MyObject();
18 }
19 }
20 }
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 }
24 return myObject;
25 }
26 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出多个线程获得的是同一个对象
8 * 使用双重检查锁功能,解决懒汉模式遇到的多线程问题
9 */
10 public static void main(String[] args) {
11 MyThread t1 = new MyThread();
12 MyThread t2 = new MyThread();
13 MyThread t3 = new MyThread();
14 t1.start();
15 t2.start();
16 t3.start();
17 }
18 }
使用静态内部类实现单例:
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 //静态内部类
7 public static class MyObjectHandler{
8 private static MyObject myObject = new MyObject();
9 }
10
11 private MyObject() {}
12
13 public static MyObject getInstance() {
14 return MyObjectHandler.myObject;
15 }
16 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 System.out.println(MyObject.getInstance().hashCode());
9 }
10 }
1 /**
2 * 测试类
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出多个线程获得的是同一个对象
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 MyThread t2 = new MyThread();
12 MyThread t3 = new MyThread();
13 t1.start();
14 t2.start();
15 t3.start();
16 }
17 }
序列化与反序列化的单例设计模式:
1 import java.io.Serializable;
2
3 /**
4 * 延迟加载单例模式类
5 */
6 public class MyObject implements Serializable{
7
8 private static final long serialVersionUID = 1L;
9
10 //静态内部类
11 public static class MyObjectHandler{
12 private static MyObject myObject = new MyObject();
13 }
14
15 private MyObject() {}
16
17 public static MyObject getInstance() {
18 return MyObjectHandler.myObject;
19 }
20 //保证反序列化拿到的对象与序列化对象是同一个对象
21 protected Object readResolve() {
22 System.out.println("调用了readResolve()方法");
23 return MyObjectHandler.myObject;
24 }
25 }
1 import java.io.File;
2 import java.io.FileInputStream;
3 import java.io.FileNotFoundException;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.ObjectInputStream;
7 import java.io.ObjectOutputStream;
8
9 /**
10 * 测试类
11 */
12 public class Run {
13 public static void main(String[] args) {
14 //将对象序列化到文件
15 try {
16 MyObject myObject = MyObject.getInstance();
17 FileOutputStream fops = new FileOutputStream(new File("myObjectFile.text"));
18 ObjectOutputStream oops = new ObjectOutputStream(fops);
19 oops.writeObject(myObject);
20 oops.close();
21 fops.close();
22 System.out.println(myObject.hashCode());
23 } catch (FileNotFoundException e) {
24 e.printStackTrace();
25 } catch (IOException e) {
26 e.printStackTrace();
27 }
28 //将文件中序列化的对象反序列化并输出到控制台
29 try {
30 FileInputStream fips = new FileInputStream(new File("myObjectFile.text"));
31 ObjectInputStream oips = new ObjectInputStream(fips);
32 MyObject readObject = (MyObject)oips.readObject();
33 oips.close();
34 fips.close();
35 System.out.println(readObject.hashCode());
36 } catch (FileNotFoundException e) {
37 e.printStackTrace();
38 } catch (IOException e) {
39 e.printStackTrace();
40 } catch (ClassNotFoundException e) {
41 e.printStackTrace();
42 }
43 }
44 }
使用静态代码块实现单例:
1 /**
2 * 延迟加载单例模式类
3 */
4 public class MyObject {
5
6 private static MyObject instance = null;
7
8 private MyObject() {}
9
10 static {
11 instance = new MyObject();
12 }
13
14 public static MyObject getInstance() {
15 return instance;
16 }
17 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 for (int i = 0; i < 5; i++) {
9 System.out.println(MyObject.getInstance().hashCode());
10 }
11 }
12 }
1 /**
2 * 测试类,测试使用静态代码块实现单例
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出多个线程获得的是同一个对象
8 */
9 public static void main(String[] args) {
10 MyThread t1 = new MyThread();
11 MyThread t2 = new MyThread();
12 MyThread t3 = new MyThread();
13 t1.start();
14 t2.start();
15 t3.start();
16 }
17 }
使用enum枚举数据类型实现单例模式:
1 import java.sql.Connection;
2 import java.sql.DriverManager;
3 import java.sql.SQLException;
4
5 /**
6 * 使用enum枚举类型实现单例模式,此处的枚举类进行了暴露,违反了职责单一原则
7 */
8 public enum MyObject {
9
10 connectionFactory;
11
12 private Connection connection;
13
14 private MyObject() {
15 try {
16 System.out.println("调用MyObject的构造方法");
17 String url = "";
18 String user = "";
19 String password = "";
20 String driverName = "";
21 Class.forName(driverName);
22 connection = DriverManager.getConnection(url, user, password);
23 } catch (ClassNotFoundException e) {
24 e.printStackTrace();
25 } catch (SQLException e) {
26 e.printStackTrace();
27 }
28 }
29
30 public Connection getConnection() {
31 return connection;
32 }
33 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 for (int i = 0; i < 5; i++) {
9 System.out.println(MyObject.connectionFactory.getConnection().hashCode());
10 }
11 }
12 }
1 /**
2 * 测试类,测试使用枚举数据类型实现单例
3 */
4 public class Run {
5
6 /**
7 * 测试结果可以看出多个线程获得的是同一个对象
8 * 枚举与静态代码块相似,在使用枚举类时,构造方法自动被调用
9 */
10 public static void main(String[] args) {
11 MyThread t1 = new MyThread();
12 MyThread t2 = new MyThread();
13 MyThread t3 = new MyThread();
14 t1.start();
15 t2.start();
16 t3.start();
17 }
18 }
完善使用enum枚举数据类型实现单例模式:
1 import java.sql.Connection;
2 import java.sql.DriverManager;
3 import java.sql.SQLException;
4
5 /**
6 * 使用enum枚举类型实现单例模式
7 */
8 public class MyObject {
9 public enum MyEnumSingletion{
10 connectionFactory;
11
12 private Connection connection;
13
14 private MyEnumSingletion() {
15 try {
16 System.out.println("调用MyObject的构造方法");
17 String url = "";
18 String user = "";
19 String password = "";
20 String driverName = "";
21 Class.forName(driverName);
22 connection = DriverManager.getConnection(url, user, password);
23 } catch (ClassNotFoundException e) {
24 e.printStackTrace();
25 } catch (SQLException e) {
26 e.printStackTrace();
27 }
28 }
29
30 public Connection getConnection() {
31 return connection;
32 }
33 }
34
35 public static Connection getConnection() {
36 return MyEnumSingletion.connectionFactory.getConnection();
37 }
38 }
1 /**
2 * 线程类
3 */
4 public class MyThread extends Thread {
5
6 @Override
7 public void run() {
8 for (int i = 0; i < 5; i++) {
9 System.out.println(MyObject.getConnection().hashCode());
10 }
11 }
12 }
1 /**
2 * 测试类,测试使用枚举数据类型实现单例
3 */
4 public class Run {
5
6 public static void main(String[] args) {
7 MyThread t1 = new MyThread();
8 MyThread t2 = new MyThread();
9 MyThread t3 = new MyThread();
10 t1.start();
11 t2.start();
12 t3.start();
13 }
14 }