单例模式四大原则:
1.构造器必须私有化,私有化后类加载一次后无法在通过其他手段创建对象。
2.通过方法或枚举获取对象,在类加载时实例化对象,且无法通过外部手段创建额外对象。
3.确保实例只有一个,尤其是多线程环境。
4.确保反序列换时不会重新构建对象。
单例模式的特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例模式分类:
饿汉模式:线程安全,但是存在资源消耗,类加载时创建对象。
/**
* 单例模式(饿汉模式)
* 线程安全的,类加载时就分配了资源
*/
public class HungrySingletonPattern {
private static HungrySingletonPattern singleton = new HungrySingletonPattern();
private HungrySingletonPattern (){
}
public static HungrySingletonPattern getInstall() throws InterruptedException {
Thread.sleep(1000);
return singleton;
}
}
测试:
public class Test {
public static void main(String[] args) {
for(int i = 0;i < 10; i++){
new Thread(()->{
try {
HungrySingletonPattern singletonPattern = HungrySingletonPattern.getInstall();
System.out.println(singletonPattern);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
com.wonder.cube.test.designmode.HungrySingletonPattern@4388eabf
可以看出是同一个对象
2.懒汉模式(又叫饱汉模式)
/**
* 单例模式(懒汉模式)
* 在用到该单例对象时才创建对象
* 多线程下单例模式无效,线程不安全
*/
public class LazySingletonPattern {
private static LazySingletonPattern singleton;
private LazySingletonPattern(){
}
public static LazySingletonPattern getInstance() throws InterruptedException {
if(singleton == null){
// 模拟做一些事情,会把线程都聚集在此,测试用
Thread.sleep(1000);
singleton = new LazySingletonPattern();
}
return singleton;
}
}
测试:
public class Test {
public static void main(String[] args) {
for(int i = 0;i < 10; i++){
new Thread(()->{
try {
LazySingletonPattern singletonPattern = LazySingletonPattern.getInstance();
System.out.println(singletonPattern);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:
com.wonder.cube.test.designmode.LazySingletonPattern@4f0c4707
com.wonder.cube.test.designmode.LazySingletonPattern@3996d457
com.wonder.cube.test.designmode.LazySingletonPattern@4f0c4707
com.wonder.cube.test.designmode.LazySingletonPattern@1e057600
com.wonder.cube.test.designmode.LazySingletonPattern@4f0c4707
com.wonder.cube.test.designmode.LazySingletonPattern@626213bf
com.wonder.cube.test.designmode.LazySingletonPattern@90472a2
com.wonder.cube.test.designmode.LazySingletonPattern@19281561
com.wonder.cube.test.designmode.LazySingletonPattern@4533731f
com.wonder.cube.test.designmode.LazySingletonPattern@616831d4
出现了多个对象,原因:
线程1在进入到 == null判断时是ture,因此进入到内部执行sleep,此时并未创建任何对象,接下来线程2进入到代码,此时==null依然成立,都阻塞在sleep中,剩下的线程也是同样的道理,当sleep结束后,进入到==null的代码中的线程都执行了一遍singleton = new LazySingletonPattern();的代码,因此出现了不同对象。
3.双重验证懒汉模式:
/**
* 双重验证懒汉模式(线程安全)
*
*/
public class LazyAgainSingletonPattern {
private static volatile LazyAgainSingletonPattern pattern;
private LazyAgainSingletonPattern(){
}
public static LazyAgainSingletonPattern getInstance(){
if(pattern == null){
synchronized (LazyAgainSingletonPattern.class){
if(pattern == null){
pattern = new LazyAgainSingletonPattern();
}
}
}
return pattern;
}
}
volatile关键字不加的话,会出现jvm乱序的问题,具体问题请百度一下,此处不做解释。
加了synchronized锁后,每个线程排队执行,就不会出现上述问题了。
测试:
public class Test {
public static void main(String[] args) {
for(int i = 0;i < 10; i++){
new Thread(()->{
try {
LazyAgainSingletonPattern singletonPattern = LazyAgainSingletonPattern.getInstance();
System.out.println(singletonPattern);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
com.wonder.cube.test.designmode.LazyAgainSingletonPattern@3996d457
4.静态内部类饿汉模式
/**
* 懒汉模式
* 静态内部类模式,线程安全的,应为对象是被static 修饰,只有一份,且在需要用到时才创建
* 缺点:无法传参
*/
public class InnerStaticHungrySingletonPattern {
private InnerStaticHungrySingletonPattern(){
}
private static class Hungry{
private static InnerStaticHungrySingletonPattern pattern = new InnerStaticHungrySingletonPattern();
protected static InnerStaticHungrySingletonPattern getInstance(){
return pattern;
}
}
public static InnerStaticHungrySingletonPattern getInstance(){
return Hungry.getInstance();
}
}
jvm在加载类的时候,不会加载该类的内部类,只有在用到该内部类的时候才会去加载该类的内部类。此方法具有了懒加载模式,同时又符合饿汉模式,唯一的缺点是不能传参数。
测试:
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
com.wonder.cube.test.designmode.InnerStaticHungrySingletonPattern@55477623
在具体项目中可以根据需求选择不同的单例模式