写在前面,给大家介绍了一些关于多线程的基础知识点。为了让大家更好的了解多线程的一些相关特性,本博客将结合一些具体的代码案例。给大家介绍一下~本篇博客给大家介绍第一个关于多线程的案例——单例模式~1.单例模式的概念所谓单例模式是一种常见的设计模式~单例模式希望:对于一些对象,在一个程序中应该只有一个实例,那么就可以使用单例模式~也就是说,在单例中mode,对象的实例化是有限制的,只能创建一个,不能创建更多~如果靠人来保证,是不靠谱的,所以借助语法,强行限制创建多个实例,所以以免程序员不小心犯错~设计模式:类似于象棋记录,是前人总结的一些固定套路,按照棋谱下棋,棋不会太差,增加了下限(对于example,chess...)~2.Java中单例模式的简单实现单例模式的实现方式有很多种。这里主要分为两大类:饿汉模式,懒汉模式~饿汉模式和懒汉模式,描述的是创建实例的时间~这两种模式不是高低之分,不是“贬义词”中的现实生活。相反,在计算机领域,“懒惰”仍然是一个褒义词。这个词表示电脑的性能比较高。在计算机中,这种思维很普遍。~比如你想知道某个信息(一个10G的大文件,存储在硬盘里),那么这时候用编辑器打开这个文件,就会有两种情况:[饿汉]阅读全部10G入内存,读取完成后,允许用户查看和修改~[懒人]只读了一点点(当前屏幕能显示的范围),继续往下看-当用户翻页时up内容~所以,如果饿了么是中文模式的话,显示时间会比较长;如果是惰性模式,显示时间会更短,效率会更高(也有可能是用户打开文件后,只看两眼就关闭了,后面的大部分都没看过),所以读那么多内存是没有意义的)~因此,我们通常认为懒人模式比饿鬼模式更有效率~当然还有刷抖音,看小说,看微信,浏览内容网上的...,我借用了懒人模式~2.1饿人模式饿人模式就是程序一启动就立马创建一个实例~这个就好比饿人,当你看到一段蛋糕,你迫不及待地想把它塞进嘴里。我们称之为“饿汉”~static关键字的来龙去脉:static虽然叫“静态”,但与字面意思无关。是的一个历史遗留问题~实际意思是“类属性/类方法”。同样,我们把非静态普通成员称为“实例属性/实例方法”~Java之所以称为“静态”,是因为C++表示“类属性”就是使用静态,Java是从C++复制过来的;而C++就是因为面向对象的引入。如果需要一种定义类属性的方式,就需要引入关键字,但是引入新关键字的成本极高,所以关键字设计的老板们都盯上了旧关键字~所以,static在中间的Hi,static本来的意思是变量放在静态内存区,但是随着时间的推移和系统的演化,“静态内存区”这个词已经不存在了,但是static关键字还在,所以“旧瓶装新酒”,就是用来表示“类属性/类方法”~这个时候“类属性/类方法”跟字面意思是不是static没有关系,随便找个老的关键字随意,现在没用了,给一个新的功能,就这样~引入新关键字成本极高的原因:写代码的时候,变量名不能和关键字重名,new的时候引入了关键字,无法确定还有谁不是引入了新的关键字作为变量名(世界上有那么多C++代码。。。)~更有可能的是,一旦引入了新的关键字,一些将编译现有代码。失败~那么火就会烧在关键字designer上~而class属性是生长在类对象上的,而类对象在整个程序中只有一个实例(JVM保证),所以类的静态成员有isonlyone~packagethread;//单例模式,饿了么的方式classSingleton{privatestaticSingletoninstance=newSingleton();//如果后面需要这个实例,需要使用getInstance方法获取实力单苗,不要去new~publicstaticSingletongetInstance(){returninstance;}//构造方法设置为private,此时其他类不能来new(通过编译器的规则保证只有一个实例对象)~privateSingleton(){}}publicclassDemo19{publicstaticvoidmain(String[]args){//饿鬼模式的调用Singletoninstance=Singleton.getInstance();单例实例2=Singleton.getInstance();System.out.println(instance==instance2);}}运行结果:如果不小心想再创建一个实例,会编译报错:总结:使用静态成员表示实例(唯一性)+构造函数私有化(阻塞对创建新实例的开放)~按照上面的代码,当加载Singleton类时,会执行实例化操作。这时候实例化的时机很早(感觉很急),我们称之为饿鬼模式~对于饿鬼模式,在多线程的情况下,会多次调用getInstance()方法,并且此方法只是一个读取操作。对于多线程的读操作,是线程安全的~2.2LazyMode懒惰模式就是程序一开始,不要急于创建实例,等到真正用到的时候,再创建实例~这也是很形象,比较“懒”,不想工作,等需要的时候再做~//懒模式类SingleLazy的实现{//这里并没有立即创建实例privatestaticSingletLazyinstance=null;//第一次调用getInstance()时,会创建实例publicstaticSingletLazygetInstance(){if(instance==null){instance=newSingletLazy();}返回实例;}//同理,创建构造函数SingletLazy(),防止该类实例化其他对象。privateSingletLazy(){}}多线程情况下,惰性模式,多次调用getInstance()方法,涉及两次读操作(读取实例是否为空,读取返回的instance值)和一个写操作(修改实例变量的值),不是线程安全的~当然,一旦创建了实例,后面的if条件语句就无法进入,此时都是读操作,是线程安全的~既然已经明确了懒人模式不是线程安全的,那么如何解决懒人模式的线程不安全问题呢?解决办法就是锁起来!!!加锁是为了保证“判断”和“修改”这组操作是原子的~//惰性模式的实现)只会创建一个实例publicstaticSingletLazygetInstance(){synchronized(SingletLazy.class){if(instance==null){实例=newSingletLazy();}}返回实例;}//同理,创建一个结构体方法SingletLazy(),防止这个类实例化其他对象。privateSingletLazy(){}}惰性模式,只是在初始情况下,会出现线程不安全的情况。实例一旦创建,此时是安全的~所以,后面调用getInstance的时候不要尝试加锁~如果使用上面的代码,不管实例是否为空(初始化与否),都会加锁,会加剧锁竞争,消耗一些不必要的资源,会大大影响效率~加锁前,需要判断实例是否为空(是否初始化),如果为空,则belocked://惰性模式的实现classSingleLazy{//这里不是立即创建实例privatestaticSingletLazyinstance=null;//第一次调用getInstance()时,会创建一个实例publicstaticSingletLazygetInstance(){if(instance==null){synchronized(SingletLazy.class){if(instance==null){instance=newSingletLazy();}}}返回实例;}//同理,创建构造函数SingletLazy(),防止该类实例化其他对象如果已经初始化了,那就继续Let'sgo~里面的if正在尝试初始化多个线程,导致锁竞争。这些竞争线程拿到锁后,我们再进一步确认是否真的需要初始化~当然上面的代码运行还是存在一些问题——有的线程在读,有的线程在写~这让我想起了——内存可见性问题~其实这里的情况和前面的不一样。每个线程都有自己的context,从逻辑上讲,寄存器的内容不应该被优化~但是,其实也不好说,也不能保证编译器会优化什么样的过程~所以,加入volatile到instance是比较健壮的做法~如果不加volatile可能没问题,但最好加volatile~//Lazy模式实现classSingletLazy{//这里没有立即创建instancevolatileprivatestaticSingletLazyinstance=无效的;//第一次调用getInstance()时只会创建实例publicstaticSingletLazygetInstance(){if(instance==null){synchronized(SingletLazy.class){if(instance==null){instance=newSingletLazy();}}}返回实例;}//同理,创建一个构造函数SingletLazy(),防止该类实例化其他对象但添加它必须是正确的)。这里先介绍一下单例模式的内容。当然Java中单例模式的实现方式还有很多,比如基于枚举,基于内部类等等~如果觉得这篇博客对你有帮助,可以一键过一遍,非常感谢~
