大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!
单例模式
- 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。
- 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
懒汉式
懒汉式:默认不会实例化,什么时候用什么时候new。
代码实例
package com.walker.test.designMode.signleton;
public class LazySingleton {
//static 修饰
private static LazySingleton instance;
//私有构造函数
private LazySingleton(){}
//static synchronized
public static synchronized LazySingleton getInstance(){
//如果实例为null则创建
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
测试结果
package singletonPattern.lazy;
public class LazyTest {
public static void main(String[] args) {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance2);
ThreadTest threadTest = new ThreadTest();
threadTest.start();
/**
* 输出结果:线程不安全
* singletonPattern.lazy.LazySingleton@677327b6
* singletonPattern.lazy.LazySingleton@14ae5a5
* singletonPattern.lazy.LazySingleton@32c3e0c4
*/
}
}
饿汉式
饿汉式:类加载的时候就实例化,并且创建单例对象。
代码实例
package com.walker.test.designMode.signleton;
/**
* 饿汉式 方式一
*/
public class HungrySingleton {
//直接实例化
private static HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
//直接返回结果
public static HungrySingleton getInstance(){
return instance;
}
}
package singletonPattern.hungry;
/**
* 饿汉式 方式二
*/
public class HungrySingleton2 {
private HungrySingleton2(){}
private static HungrySingleton2 instance=null;
static {
instance=new HungrySingleton2();
}
public static HungrySingleton2 getInstance(){
return instance;
}
}
测试结果
package singletonPattern.hungry;
public class HungryTest {
public static void main(String[] args) {
HungrySingleton1 instance = HungrySingleton1.getInstance();
System.out.println(instance);
HungrySingleton1 instance2 = HungrySingleton1.getInstance();
System.out.println(instance2);
/**
* 执行结果: 相同,线程安全
* singletonPattern.hungry.HungrySingleton@677327b6
* singletonPattern.hungry.HungrySingleton@677327b6
*/
}
}
双检锁/双重校验锁
- 这个可以说是懒汉式的进化版,增加了volatile实现可见性,以及synchronized实现了线程安全。
代码实例
package com.walker.test.designMode.signleton;
public class DoubleLockSingleton {
//volatile
private static volatile DoubleLockSingleton instance=null;
//构造方法不提供外部使用,因此是private
private DoubleLockSingleton(){}
private static DoubleLockSingleton getInstance(){
//先判null
if(instance==null){
//对该类进行加锁
synchronized (DoubleLockSingleton.class){
//再判null,因为可能有些线程在加锁之后已经为非null,这个时候就直接返回即可
if(instance==null){
instance=new DoubleLockSingleton();
}
}
}
return instance;
}
}
测试结果
package singletonPattern.doubleCheck;
public class DoubleCheckLockTest {
public static void main(String[] args) {
DoubleCheckLock instance = DoubleCheckLock.getInstance();
System.out.println(instance);
DoubleCheckLock instance1 = DoubleCheckLock.getInstance();
System.out.println(instance1);
DoubleCheckLock instance2 = DoubleCheckLock.getInstance();
System.out.println(instance2);
/**
* singletonPattern.doubleCheck.DoubleCheckLock@677327b6
* singletonPattern.doubleCheck.DoubleCheckLock@677327b6
* singletonPattern.doubleCheck.DoubleCheckLock@677327b
*
* 线程安全
*/
}
}
ThreadLocal
ThreadLocal是一种容器,将对象进行拷贝,相当于以空间换时间
package singletonPattern.threadlocal;
public class Singleton {
private Singleton(){}
/**
* 使用final和static
* final代表这个变量引用不可以被修改
* 空间替换时间
*/
private static final ThreadLocal<Singleton> instance=new ThreadLocal<Singleton>(){
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
public static Singleton getInstance(){
return instance.get();
}
}
测试
package singletonPattern.threadlocal;
public class Test {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
System.out.println(instance1);
Singleton instance2 = Singleton.getInstance();
System.out.println(instance2);
/**
* singletonPattern.threadlocal.Singleton@677327b6
* singletonPattern.threadlocal.Singleton@677327b6
* 返回结果一致,线程安全
*/
}
}
枚举
package singletonPattern.enums;
/**
* 枚举类的成员变量其实都是静态类型的,并且是在
* * 静态代码块中实例化的,有点像饿汉模式,也是天然线程安全的
*/
public enum Singleton {
INSTANCE;
Singleton() {
}
}
懒汉式和饿汉式区别: 4
- 实例化方面:
- 懒汉式默认不会实例化,外部什么时候调用什么时候new。
- 饿汉式在类加载的时候就实例化,并且创建单例对象。
- 线程安全方面:
- 饿汉式线程安全 (在线程还没出现之前就已经实例化了,因此饿汉式线程一定是安全的)。
- 懒汉式线程不安全( 因为懒汉式加载是在使用时才会去new 实例的,那么你去new的时候是一个动态的过程,是放到方法中实现的,比如:public static synchronized Lazy getInstance(){ if(lazy==null){ lazy=new Lazy(); } 如果这个时候有多个线程访问这个实例,这个时候实例还不存在,还在new,就会进入到方法中,有多少线程就会new出多少个实例。一个方法只能return一个实例,那最终return出哪个呢?是不是会覆盖很多new的实例?这种情况当然也可以解决,那就是加同步锁,避免这种情况发生) 。
- 执行效率上:
- 饿汉式没有加任何的锁,因此执行效率比较高。
- 懒汉式一般使用都会加同步锁,效率比饿汉式差。
- 性能上:
- 饿汉式在类加载的时候就初始化,不管你是否使用,它都实例化了,所以会占据空间,浪费内存。
- 懒汉式什么时候需要什么时候实例化,相对来说不浪费内存。
实战应用
- 线程池
- 创建jedis