单例模式-多线程环境
2018-09-05 07:48:19来源:博客园 阅读 ()
单例模式-多线程环境
单例-立即加载:
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 }
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 设计模式-委派/策略模式 2020-06-09
- 最详细的java多线程教程来了 2020-06-08
- 系统化学习多线程(一) 2020-06-08
- 深入理解:设计模式中的七大设计原则 2020-06-07
- 设计模式---类之间的关系知多少 2020-06-07
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash