单例模式是我在学习多线程的时候理解的。以前总是在一些代码中看到 getInstance方法,然后也知道是什么,但是没有具体的研究过它,所以这篇博文也算是我对单例模式的理解和总结吧
定义
单例模式是一种常用设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案
常见的实现单例模式的方法有懒汉式和饿汉式两种,但是其他的我也实现了,参考了: 《JAVA多线程编程核心技术》
+++++++++以下的每个实现,大家最好的手动实现,这样才能更好的理解++++++++++++
先介绍饿汉式的单例模式 :
饿汉式单例模式
饿汉单例模式是立即加载的,它的优点是 在还没有掉用方法的时候,对象就已经存在了,这样做的好处是节省了创建对象的时间
缺点是 : 因为对象在加载类的时候就已经存在内存之中,所以会占用较多的空间
实现一 :
饿汉式的单例模式就是先创建一个对象,要用的时候直接调用就行了,不需要担心线程安全问题
public class Hungry {
安全问题
private static Hungry hungry = new Hungry();
private Hungry(){}
public static Hungry getInstance(){
try {
//放大错误,看是否出现不同现象
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return hungry;
}
public static void main(String[] args) {
//以Hashcode值判断是否是一个实例
Thread th1 = new Thread(()->{
System.out.println(Hungry.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Hungry.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Hungry.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
实现二:
以静态内部类的方式来实现饿汉式单例模式
静态内部类在外部类加载的时候也不会加载,只有调用实例方法的时候才会加载
public class Hungry2 {
private Hungry2(){}
private static class InHungry2{
private static Hungry2 hungry2 = new Hungry2();
}
public static Hungry2 getInstance(){
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return InHungry2.hungry2;
}
public static void main(String[] args){
Thread th1 = new Thread(()->{
System.out.println(Hungry2.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Hungry2.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Hungry2.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
懒汉式单例模式
实现一 :
懒汉式的单例模式是延迟加载的,对象在被调用的时候才会被加载,所以这样会影响程序执行的时间
并且懒汉式在多线程访问的情况下是不安全的
public class Lazy {
private static Lazy lazy = null;
private Lazy(){}
//这样的获取实例的方法是不安全的,仅在单线程的情况下有用
public static Lazy getInstance() {
if (null == lazy) {
//把注释去掉,测试多线程的情况,肯定是不安全的
// try {
// Thread.currentThread().sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
lazy = new Lazy();
}
return lazy;
}
public static void main(String[] args){
Thread th1 = new Thread(()->{
System.out.println(Lazy.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Lazy.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Lazy.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
实现二 :
懒汉式改进
因为多线程的情况下,普通的懒汉式单例模式是不安全的,所以可以给获取实例的方法上锁,这样就可以使它变成安全的
public class Lazy2 {
private static Lazy2 lazy2 = null;
private Lazy2(){}
//给整个方法上锁,虽然安全了,但是效率却降低了
public static synchronized Lazy2 getInstance(){
if(null == lazy2) {
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazy2 = new Lazy2();
}
return lazy2;
}
public static void main(String[] args){
Thread th1 = new Thread(()->{
System.out.println(Lazy2.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Lazy2.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Lazy2.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
实现三 :
虽然加了synchronized关键字的懒汉式单例模式安全了,但是执行的效率变低了
所以可以利用Double check Locking双重检查的方法使他执行的效率提高
public class Lazy3 {
private static Lazy3 lazy3 = null;
private Lazy3(){}
public static Lazy3 getInstance(){
//双重检查
if(null == lazy3){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//多个线程在此处等待,只要一个线程获取了锁,创建了对象,另外的线程就可以
//直接使用对象,所以大大提高了效率
synchronized(Lazy3.class){
if(null == lazy3)
lazy3 = new Lazy3();
}
}
return lazy3;
}
public static void main(String[] args) {
Thread th1 = new Thread(()->{
System.out.println(Lazy3.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Lazy3.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Lazy3.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
实现四 :
用静态初始化块来实现懒汉式单例模式
public class Lazy4 {
private static Lazy4 hungry3 = null;
private Lazy4(){}
//静态初始化块和类一起加载的,所以不用担心线程安全问题
static {
hungry3 = new Lazy4();
}
public static Lazy4 getInstance(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return hungry3;
}
public static void main(String[] args) {
Thread th1 = new Thread(()->{
System.out.println(Lazy4.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(Lazy4.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(Lazy4.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
利用枚举实现
使用枚举内部类创建单例模式
因为每个枚举类对象都是唯一的,所以不用担心线程安全问题
public class EnumSingle {
public EnumSingle(){}
private enum Single{
//枚举对象
SING;
private EnumSingle enu = null;
private Single(){
enu = new EnumSingle();
}
private EnumSingle getInstance(){return enu; }
}
public static EnumSingle getInstance(){
return Single.SING.enu;
}
public static void main(String[] args){
//以Hashcode值判断是否是一个对象
Thread th1 = new Thread(()->{
System.out.println(EnumSingle.getInstance().hashCode());
});
Thread th2 = new Thread(()->{
System.out.println(EnumSingle.getInstance().hashCode());
});
Thread th3 = new Thread(()->{
System.out.println(EnumSingle.getInstance().hashCode());
});
th1.start();
th2.start();
th3.start();
}
}
综上,希望大家还是好好的理解,然后多上手,理解,因为一个单例模式就有这么多巧妙的实现,以前听别人说JAVA很简单,但是真正学起来,哪有说的那么容易
talk is cheap , show me the code