B站学习做的笔记
单例模式就是采取一定的方法保证整个软件
统里面对于某个类只能存在一个实例
并且该类只提供一个取对象实例的方法(静态方法)
饿汉式(静态常量)
饿汉式(静态代码块)
懒汉式(线程不安全)
懒汉式(线程安全 同步方法)
双重检查
静态内部类
枚举
1静态常量
步骤
1. 构造器私有化(防止new )
2. 类的内部创建对象
3. 向类的外面暴露一个静态的公共方法getInstance
4. 代码实现
package com.设计模式.单例模式;
public class singleton1 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
//饿汉式(静态常量)
class Singleton{
//构造器私有化
private Singleton() {
}
//内部创建实例
private final static Singleton singleton = new Singleton();
//提供返回方法
public static Singleton getInstance(){
return singleton;
}
}
325040804
325040804
hascode相同
4 结论:这种方法可能会导致内存浪费.
静态代码块
package com.设计模式.单例模式;
public class Test {
public static void main(String[] args) {
Singleton2 singleton2 = Singleton2.getInstance();
Singleton2 singleton3 = Singleton2.getInstance();
System.out.println(singleton2.hashCode()+"----"+singleton3.hashCode());
}
}
class Singleton2{
private static Singleton2 singleton2;
private Singleton2() {
}
static {
singleton2 = new Singleton2();
}
public static Singleton2 getInstance(){
return singleton2;
}
}
优缺点 同上
1 线程不安全
package com.设计模式.单例模式;
public class Test2 {
public static void main(String[] args) {
SingletonTest test = SingletonTest.getInstance();
SingletonTest test2 = SingletonTest.getInstance();
System.out.println(test.hashCode()+"__"+test2.hashCode());
}
}
class SingletonTest{
private static SingletonTest Instance;
private SingletonTest(){ }
//提供一个静态方法 用到方法再去创建instance
public static SingletonTest getInstance(){
if (Instance ==null){
Instance = new SingletonTest();
}
return Instance;
}
}
优缺点
1.起到了lazy loading 的效但是只能在单线程下使用
2 多线程下 一个线程进入if 判断 还未来得及向下执行 另一个线程也进去判断 这样会产生多个实例 (多线程下不可使用)
2 线程安全
public class Test {
public static void main(String[] args) {
Singleton instacne = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(instacne == singleton2);
}
}
class Singleton{
private static Singleton instance;
private Singleton(){}
//加入同步处理
public static synchronized Singleton getInstance(){
if (instance==null){
instance = new Singleton();
}
return instance;
}
}
问题:
1 解决了线程不安全的问题
2 效率低了 线程要获得实例的时候getInstance 都要同步
注: 如果在这使用同步代码块 起不到线程安全的作用
public class test {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
}
}
class Singleton{
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if (singleton ==null){
synchronized (Singleton.class){
if (singleton ==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
//对象创建可能指令重排,创建空间,指向空间,然后才初始化,volatile可保证有序,可见
这里必须加volalite的原因:
new 对象的过程分为三步:
1.分配空间
2.初始化对象
3.指向对象内存地址。2和3可能被编译器自动重排,导致判断非空但是实际拿的对象还未完成初始化
注: new 操作中初始化内存空间和指向 内存空间这两步操
作会根据情况不同,执行顺序不同,如果先
是指向内存空间执行,这时对
象不为空,另一个线程有
可能进入并返回未初始化的对象
优缺点
1 Double-check 保证线程的安全性
2 线程安全 延迟加载 效率较高
public class Test {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
}
}
class Singleton{
private Singleton(){}
private static class singletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return singletonInstance.INSTANCE;
}
}
1 使用了类装载的机制保证初始化实例只有一个线程
2静态内部类在类被装载时候并不会立即实例化 而是在
需要实例化的时候调用getInstance() 才会装载内部类完成实例化
3 类的静态属性在第一次加载类的时候会初始化 jvm
帮助我们保护l线程安全
4 利用静态内部类实现延迟加载 效率高
public class test {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton.hashCode()+"___"+singleton2.hashCode());
singleton.HELLO();
}
}
enum Singleton{
INSTANCE;
public void HELLO(){
System.out.println("HEllO");
}
}
避免多线程同步问题 还能防止反序列化重新创建新的对象