设计模式之--单例模式
单例模式--导读
在我们编程过程中我们会经常碰到一些对象,他们要频繁的使用,而我们为了方便于是便频繁的对这个对像进行新建,当然这对我们编程的确是很方便
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。但是,这同时会造成很大的资源浪费。所以我们想出了一个限制资源的方法,那就是只能新建一次,而不允许第二次新建,但是我们又需全局对该类的引用
所以我们干脆使得该类不需要在其他类中进行实例化,可以对该类进行直接引用,这便是单例模式。
单例模式--定义
从上面的导读我们可以理解到,单例模式的特点为:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式--模型
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例模式--代码实现
对于单例模式我们想的方法必然是私有化构造方法,同时使用一个静态变量,让其他类无法新建该对象,这样便可以确保该对象只能产生一个
当要引用时先判断静态变量中是否为空,为空则新建,不为空则不新建,私有化构造方法可以确保其他类不能对该类进行实例化
下面进行代码展示:
Singleton.java单例的类
package Singleton; //懒汉式单例 public class Singleton { //定义静态变量,用于判断是否已经被实例化 private static Singleton uniqueSingleton; //私有化构造器,防止其他类进行实例化 private Singleton() { } //返回所需的对象 public static Singleton getInstance() { if(uniqueSingleton==null) uniqueSingleton=new Singleton(); return uniqueSingleton; } public void showMessage() { System.out.println("hello world"); } }
client.java客户端的使用
package Singleton; public class Client { public static void main(String[] args) { // TODO Auto-generated method stub Singleton single=Singleton.getInstance(); single.showMessage(); } }
但是这样可能出现一个问题当我们另外开个线程的时候就会发现,这样的单例根本没有用。以下例子
package Singleton; public class Singleton { //定义静态变量,用于判断是否已经被实例化 private static Singleton uniqueSingleton; //私有化构造器,防止其他类进行实例化 private Singleton() { } //返回所需的对象 public static Singleton getInstance() { //需要消耗资源,和时间 try { if(uniqueSingleton==null) { Thread.sleep(300); uniqueSingleton=new Singleton(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return uniqueSingleton; } public void showMessage() { System.out.println("hello world"); } }
客户端采用多线程使用该单例:
package Singleton; public class Client extends Thread { @Override public void run() { System.out.println(Singleton.getInstance().hashCode()); } public static void main(String[] args) { // TODO Auto-generated method stub Client[] mts = new Client[10]; for(int i = 0 ; i < mts.length ; i++){ mts[i] = new Client(); } for (int j = 0; j < mts.length; j++) { mts[j].start(); } } }
结果就会发现单例模式根本就没有用:
可以从图中的哈希地址发现根本就不是同一个对象,这是因为:
线程1在判断完s==null后突然交换了cpu的使用权,变为线程2执行,由于s仍然为null,那么线程2中就会创建这个Singleton的单例对象。之后线程1拿回cpu的使用权,而正好线程1之前暂停的位置就是判断s是否为null之后,创建对象之前。这样线程1又会创建一个新的Singleton对象
以下又三种方法解决该单例问题
1.饿汉式
package Singleton; //饿汉单例 public class Singleton { //因为在类加载时就已经实现实例化,所以不用担心线程问题 private static Singleton uniqueSingleton=new Singleton(0;//但是在加载时可能会消耗更多时间 //私有化构造器,防止其他类进行实例化 private Singleton() { } //返回所需的对象 public static Singleton getInstance() { uniqueSingleton=new Singleton(); return uniqueSingleton; } public void showMessage() { System.out.println("hello world"); } }
2.线程同步
出现了上述问题时,我们就应该要考虑到同步(synchronized)问题了,当一个线程获得锁的时候其他线程就只能在外等着 ,这样就能解决线程不安全的问题了
package Singleton; //懒汉式单例 public class Singleton { //定义静态变量,用于判断是否已经被实例化 private static Singleton uniqueSingleton; //私有化构造器,防止其他类进行实例化 private Singleton() { } //返回所需的对象 public synchronized static Singleton getInstance() { if(uniqueSingleton==null) uniqueSingleton=new Singleton(); return uniqueSingleton; } public void showMessage() { System.out.println("hello world"); } }
3.双重校验锁
