什么是单例模式:一个类只允许产生一个实例化对象
特点:构造方法私有化,外部无法产生新的实例化对象,只能通过static方法取得对象实例化
首先构造方法私有化就外部无法产生新的实例化对象,所以就只能在类的内部进行实例化
private Singleton singleton=new Singleton();
但是此时singleton对象是一个普通的属性,所有的普通属性必须要在实例化对象的时候再能进行内存空间的分配,而现在外部是无法进行实例化对象的,所以必须想一个办法,可以在Singleton没有实例化对象产生的时 候,也可以将singleton进行使用。此时我们就应该想到用static关键字。(可以去了解一下static关键字的用法)
class Singleton
{
private static Singleton singleton=new Singleton();
//构造方法私有化
private Singleton(){ }
public void print()
{
System.out.println("hello world ");
}
}
以上虽然可以取得Singleton类的实例化对象,但是对于类中属性应该使用private进行封装,要想取得private属性, 应该提供getter()方法。由于此时访问的是static属性,并且这个类无法在外部提供实例化对象,因此应该提供一个 static的getter()方法
class Singleton
{
private static final Singleton singleton=new Singleton();
//构造方法私有化
private Singleton()
{}
public static Singleton getSingleton()
{
return singleton;
}
public void print()
{
System.out.println("hello world ");
}
}
以上是根据饿汉式单例模式的分析。
以下是俩种单例模式的代码:
1.饿汉式单例模式:一上来就直接实例化对象
class Singleton
{
private static final Singleton singleton=new Singleton();
//构造方法私有化
private Singleton()
{
}
public static Singleton getSingleton()
{
return singleton;
}
public void print()
{
System.out.println("hello world ");
}
}
public class 饿汉式 {
public static void main(String[] args) {
//static修饰的方法通过类名.方法名调用
Singleton singleton=Singleton.getSingleton();
singleton.print();
}
}
重要总结:对于饿汉模式来说,多线程同时调用getSingleton(),由于getSingleton()里只做了一件事:读取instance实例的地址,也就是多个线程在同时读取同一个变量,并没有构成多个线程同时修改同一个变量这一情况,所以说饿汉模式是线程安全的,不需要加锁。
2.懒汉式单例模式:第一次使用Singleton对象的时候才会为其产生实例化对象的操作
class Singleton1
{
private static Singleton1 instannce;
//构造方法私有化
private Singleton1() { }
public static Singleton1 getSingleton()
{
if (instannce==null)
{
instannce=new Singleton1();
}
return instannce;
}
public void print()
{
System.out.println("hello world ");
}
}
public class 懒汉式 {
public static void main(String[] args) {
Singleton1 s=Singleton1.getSingleton();
s.print();
}
}
懒汉模式会造成了多个线程同时修改同一个变量这一情况,所以说懒汉模式是线程不安全的,此时采用加锁来解决,大家第一反应是直接用synchronized修饰方法,但是synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了,此时就有了双重检验锁,但是双重校验会延迟加载,此时就需要指令重排优化,所谓指令重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快此时就需要voliate关键字。具体代码如下:
class Singleton
{
private volatile static Singleton instance;
private Singleton(){};
//双重检验-synchronized修饰的同步方法比一般方法要慢很多,如果多次调用getInstance(),累积的性能损耗就比较大了
// 因此就有了双重校验锁,但是双重校验会延迟加载,此时就需要指令重排优化
// 所谓指令重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快----voliate关键字
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}