单例设计模式八种

package 单例设计模式;

import com.sun.org.apache.bcel.internal.generic.FieldOrMethod;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import org.omg.CORBA.Current;

/**
 * @author lxy
 * @date 2020/12/5 11:19
 **/
public class SingleTest {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("----------方式一饿汉式:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = FirstSingle.getStu();
                System.out.println("方式一" + student1.hashCode());//793922417
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = FirstSingle.getStu();
                System.out.println("方式一" + student2.hashCode());//793922417
            }
        }.start();

        System.out.println("----------方式二懒汉式:线程不安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = SecondSingle.getStudent();
                System.out.println("方式二" + student1.hashCode());//1521612351
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = SecondSingle.getStudent();
                System.out.println("方式二" + student2.hashCode());//428999437
            }
        }.start();

        System.out.println("----------方式三懒汉式+方法上+锁:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = ThreeSingle.getStudent();
                System.out.println("方式三" + student1.hashCode());//1873649679
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = ThreeSingle.getStudent();
                System.out.println("方式三" + student2.hashCode());//1873649679
            }
        }.start();

        System.out.println("----------方式四匿名内部类:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = FourSingle.getStu();
                System.out.println("方式四" + student1.hashCode());//880702409
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = FourSingle.getStu();
                System.out.println("方式四" + student2.hashCode());//880702409
            }
        }.start();

        System.out.println("----------方式五匿双重检索:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = FiveSingle.getStudent();
                System.out.println("方式五" + student1.hashCode());//880702409
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = FiveSingle.getStudent();
                System.out.println("方式五" + student2.hashCode());//880702409
            }
        }.start();

        System.out.println("----------方式六匿双重检索:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student1 = SixSingle.getStudent();
                System.out.println("方式六" + student1.hashCode());//591353948
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Student student2 = SixSingle.getStudent();
                System.out.println("方式六" + student2.hashCode());//591353948
            }
        }.start();

        System.out.println("----------方式七匿双重检索:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                SevenSingle sevenSingle1 = SevenSingle.getInstance();
                System.out.println("方式七" + sevenSingle1.hashCode());//498974849
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                SevenSingle sevenSingle2 = SevenSingle.getInstance();
                System.out.println("方式七" + sevenSingle2.hashCode());//498974849
            }
        }.start();

        System.out.println("----------方式8匿双重检索:线程安全---------------");
        new Thread() {
            @Override
            public void run() {
                super.run();
                Map<String, Student> sevenSingle1 = Single.getStudent();
                System.out.println("方式八" + sevenSingle1.get("student").getClass().hashCode());//1054874991
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                Map<String, Student> sevenSingle2 = Single.getStudent();
                System.out.println("方式八" + sevenSingle2.get("student").getClass().hashCode());//1054874991
            }
        }.start();


    }
}

class Student {

}

/**
 * 所有单例模式,构造器私有化,目的,不允许在外部创建对象 单例模式 1 饿汉式,类初始化时就创建该对象,因为是静态成员变量 线程安全
 */
class FirstSingle {

    private static final Student stu = new Student();

    /**
     * 构造器私有化防止在外边创建对象
     */
    private FirstSingle() {
    }

    public static Student getStu() {
        /**加延时是为了延时出现线程阻塞,让两个线程都能够进到if语句里*/
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}

/**
 * 单例模式2 懒汉式,类初始化时不创建,等调用创建它的方法是再创建该对象 该方法线程不安全,需要加锁
 */
class SecondSingle {

    private static Student stu = null;

    /**
     * 构造器私有化防止在外边创建对象
     */
    private SecondSingle() {
    }

    public static Student getStudent() {
        if (stu == null) {
            /**加延时是为了延时出现线程阻塞,让两个线程都能够进到if语句里*/
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            stu = new Student();
        }
        return stu;
    }
}

/**
 * 方式三:懒汉式线程安全+方法枷锁机制
 */
class ThreeSingle {

    private static Student student;

    /**
     * 构造器私有化防止在外边创建对象
     */
    private ThreeSingle() {
    }

    public static synchronized Student getStudent() {
        if (student != null) {
            return student;
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        student = new Student();
        return student;
    }
}

/**
 * 方式四 匿名内部类 线程安全
 */
class FourSingle {

    /**
     * 匿名内部类,在这里声名该对象 使⽤类的静态内部类实现的单例模式,
     * 既保证了线程安全有保证了懒加载,同时不会因为加锁的⽅式耗费性能。
     * 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,
     * 也就是⼀个类的构造⽅法在多线程环境下可以被正确的加载。
     * 此种⽅式也是⾮常推荐使⽤的⼀种单例模式
     */
    public static class FoursSingle {

        private static Student stu = new Student();
    }

    /**
     * 构造器私有化,防止创建该对象
     */
    private FourSingle() {
    }

    /**
     * 来一个返回 Student 对象的方法
     */
    public static Student getStu() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return FoursSingle.stu;
    }
}

/**
 * 方式五: 双重检索:线程安全 双锁的⽅式是⽅法级锁的优化,减少了部分获取实例的耗时。同时这种⽅式也满⾜了懒加载。
 */
class FiveSingle {

    private static Student student = new Student();

    private FiveSingle() {
    }

    public static Student getStudent() {
        /**第一次检索,如果非空直接返回该对象*/
        if (student != null) {
            return student;
        }
        synchronized (FiveSingle.class) {
            if (student == null) {
                student = new Student();
            }
        }
        return student;
        /**如果student 该对象为空 ,则进入下面的代码,且如果是多个线程,则其它线程都要在 synchronized 外面去等候
         *等锁内的线程执行完毕出去的时候,其他线程再进来时 student 对象也不为空了,此时其他线程无法进入if语句内部
         *此处切记:一定要把锁放在if的外面,如果将锁放在if的里边,那么锁就失去了意义
         *错误写法
         *if(student == null){
         *  synchronized (FiveSingle.class){
         *      student = new Student();
         *   }
         }*/

    }
}

/**
 * CAS算法 线程安全 「AtomicReference」
 * <p>
 * java并发库提供了很多原⼦类来⽀持并发访问的数据安全性;AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference 。AtomicReference
 * 可以封装引⽤⼀个V实例,⽀持并发访问如上的单例⽅式就是使⽤了这样的⼀个 特点。使⽤CAS的好处就是不需要使⽤传统的加锁⽅式保证线程安全,⽽是依赖于CAS的忙等算法,依赖
 * 于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外 的开销,并且可以⽀持较⼤的并发性。当然CAS也有⼀个缺点就是忙等,如果⼀直没有获取到将会处于死循环中。
 */
class SixSingle {

    private static AtomicReference<Student> stu = new AtomicReference<>();

    private SixSingle() {
    }

    public static Student getStudent() {

        Student student = stu.get();

        if (student != null) {
            return student;
        }
        /**这个方法,看字面意思应该可以猜出来
         * 先比较 stu 是否等于空 如果是空就创建该对象将对象值赋给 stu
         * 相当于
         * if(stu == null){
         *     stu = new Student();
         * }
         * */
        stu.compareAndSet(null, new Student());

        return stu.get();


    }
}

/**
 * 使用枚举类:最好的方式->可以避免反序列化的 Java反射可以通过一个class文件,将该文件加载到内存然后,new一个实例出来 前面的方式也可以通过一些手段挡住反射去创建对象
 * <p>
 * 枚举类不能被反序列化的原因是因为它没有构造方法,反序列化返回的只是这样一个值, 根据这个值来查询到它对应的对象,而这个对象正是我们通过单例创建的对象
 * <p>
 * 得出结论该方法确实是非常完美,最完美没有之一
 */
enum SevenSingle {
    SevenSingle;

    public static SevenSingle getInstance() {
        return SevenSingle;
    }
}

/**
 * 方式八 线程安全 JUC下有很多集合类的容器用来保证多线程访问线程安全
 */
class Single {

    private static Map<String, Student> student = new ConcurrentHashMap<>();

    public static Map<String, Student> getStudent() {
        if (student.get("student") != null) {
            return student;
        }
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        student.put("student", new Student());
        return student;
    }
 }

/**

  • 虽然只是⼀个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这⾥包括 了;懒汉、饿汉、线程是否安全、静态类、内部类、加锁、等等。在平时的开发中如果可以确保此类是全局可⽤不需要做懒加载,那么直接创建并给外部调⽤即可。
    
  • 但如果是很多的类,有些需要在⽤户触发⼀定的条件后(游戏关卡)才显示,那么⼀定要⽤懒加载。线程的安全上可以按需选择。建议在学习的过程中⼀定要加以实践,否则很难完完整整的掌握⼀整套的知识体系。例如案例中的 出现的 Effective

  • Java ⼀书也⾮常建议⼤家阅读。另外推荐下这位⼤神的

    Github:https://github.com/jbloch

/**CAS算法

  • 上面我们提到了CAS算法

  • 那么给大家讲解一下啊

  • 首先有两个线程 A和 B

  • 有个主内存,里面有个值 value = 3;

  • 线程A 和线程B 要同时操作 value = 3 ,

  • 这两个线程首先将这个 value 拷贝回自己的内

*存去操作,而且会有个快照值,

*记录现在主内存的值是 3

  • 那么线程 A 将值 改为 5 写回主内存,在写回主

*内存之前,先检查一下现在内 存中的value是否

*还是 3,如过还是 3 那么就将 5 写入到主内存中

  • 这时候,主内存中的值变成了 5,线程 B 它要将

*value 改成 10 ,在写入主内存前,它也会先判断

*一下,结果发现主内存中的值是 5 了,因为他被

*线程A 修改了嘛,所以线程 B 必须重新读取内

*存中的新值重新操作, 这样的话有一种最遭的

*情况就是,线程 B 在往主内存中写值之前,主内

*存中的 值永远被其他线程修改过,那么线程B

*就相当于一直在等待,什么也没干关于CSA算

*法就先讲这么多,其实这个算法还是会有问题

*例如ABA问题,本次就不说那么多,以后我们单

*独说这个CAS算法的知识点

*说的这么多关于CSA的东西,可能有点复杂

*给大家画了个图,供大家参考

在这里插入图片描述

想了解更多技术内容欢迎关注我的公众号奥

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八月的雨@七月的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值