Java单例设计模式
一. 概念
1. 定义: 设计一个类,这个类确保只能有一个实例对象,在内存中只存在一个对象,而且向整个系统提供这个对象,成为单例设计模式。
2. 如何保证对象的唯一性
A. 为避免建立过多的该类对象,应该首先禁止其他程序建立该类对象
B. 还为了让其他程序可以访问到该类的对象,只好在本类中,自定义一个对象,为避免外部直接访问这个对象,要将其私有化。
C. 为了方便其他程序对自定义对象的访问,可以对外提供一些公共访问方式。
3. 代码如何体现保证对象唯一性?
A. 将构造方法私有化,这样外部就不能使用构造方法new对象了。
B. 在类中创建一个本类对象,为了防止外部访问使用这个对象,必须将其私有化,对外屏蔽。
C. 提供一个publicstatic修饰的方法让外部可以通过这个方法获得到本类这个自定义的对象。
4. 懒汉式和饿汉式
饿汉式(此时的对象是随着类加载而加载)
package com.day08;
public class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2);
}
}
class Single
{
//创建一个本类的对象,使用private static修饰
private static Single s = new Single();
//将构造方法私有化
private Single(){};
//对外提供一个public static的方法,可以获得该类对象。
public static Single getInstance()
{
return s;
}
}
最后打印的结果为: true
懒汉式(类进内存,对象还没有存在,只有调用了getSingle方法时,才建立对象)
package com.day08;
public class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2);
}
}
class Single
{
private static Single s = null;
private Single(){};
public static Single getInstance()
{
if( s == null)
{
s = new Single();
}
return s;
}
}
打印结果也是true
但是对于懒汉式这种写法,我们不难发现,有一些问题存在,我们发现当一个线程方法时,是没有任何问题,但是多个线程发生并发访问就存在问题了。假如线程1判断完if(s ==null)之后就被CPU切换到另一个线程,线程执行,进来也做完了if(s ==null)判断,此时线程1得到了CPU的执行权,就去new了一个对象,然后线程2也new了一个新的对象,所以懒汉式的对象延迟加载导致了多线程并发访问产生多个不同对象的问题发生。所以既然是考虑到多线程,当然我们就要用到多线程同步语句去控制并发访问了。
public class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2);
}
}
class Single
{
private static Single s = null;
private Single(){};
public static Single getInstance()
{
if( s == null)
{
//这里的锁不能使用this,静态方法不能使用this。
synchronized(Single.class)
{
if(s == null)
{
s = new Single();
}
}
}
return s;
}
}
synchronized是java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。在这个例子中,比如说,当A线程执行时,当读到第二个if(s1==null) 时,可能就停在这了,然后cpu再执行B线程,B读到第一个if(s1==null)这停下了,因为加上synchronized后,A进去就相当于将其他的调用锁在外面的语句上了,要先执行完A,那么A执行完后,就已经创建了一个对象;当B再读到第二个if(s1==null)的时候不符合就直接结束了。如果再有其他C或D等调用的时候,就直接不符合第一个(s1==null)的条件,所以直接返回s。在这里,我们再来看看关于懒汉式的多线程问题。
还有一种多线程的写法,是一种效率教为低的写法:package com.day08;
public class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2);
}
}
class Single
{
private static Single s = null;
private Single(){};
public static synchronized Single getInstance()
{
if( s == null)
{
s = new Single();
}
return s;
}
}
在这两种方式中,含有双重判断(称为第一种,无双重判断的为第二种)的效率更高,为什呢?虽然第一种和第二种都要先判断一下,但是对于第一种,第一个线程执行完后,s不为null了,那么后面只需要判断s是否为null即可,而对于第二种,要先判断锁,锁里没有线程,再进入,然后再判断一下s是否为null,这样一来,就要判断两次,所以,效率会更低。所以,对于双重判断,是可以提高效率的。
问题是解决了,但是相比之下,还是第一种饿汉式的单例设计模式更好一些,是一种建议使用的方式