java.单例类懒汉式线程安全问题

来源:互联网 发布:英语短语软件 知乎 编辑:程序博客网 时间:2024/06/05 19:07

鄙渣渣最近在看多线程,在看线程安全问题的时候,不论是学习视频中强调,还是网上搜的单例类、同步、线程安全的面试题中,都有单例类懒汉式线程安全问题。然后又搜了搜,发现有大牛博客上面写了怎么处理懒汉式线程安全问题,却没有说明理由,所以渣渣我结合自己体会写一下。

先小小的介绍一下,单例类是什么
单例类是一种设计思想
简单的基本思想:私有化结构体,通过判断建立的实例是否为空来判断是否能创建对象。使获取对象的途径不是经过结构体,而是通过定义的get函数。

构建单例类的三个基本:
1.只能有一个实例
2.单例类本身必须要建立这个实例
3.要给其他其他对象提供这一实例

单例类有有两个主流格式,饿汉式和懒汉式,实例代码如下:

饿汉式:

class Single    {    private static final Single f = new Single();    private Single(){};    public static Single get()    {        return f;    }    }    public class Demo {    public static void main(String[] args) {        Single f =Single.get();  //因为饿汉式在一开始就建立的对象,所以这里只要通过get方法获取引用变量就可以了    }    }

懒汉式:

    class Singleton    {    private Singleton(){}     //私有化结构体    private static Singleton single;   //建立一个变量缓存创建的实例    public static Singleton get()        //用来判断是否建立对象    {    if (single == null)    {        single =new Singleton();    }     return single;    }    }    public class person    {    public static void main(String[] args)    {        Singleton f = Singleton.get();    //获取对象不通过结构体,而是通过get方法    }    }

饿汉式因为在一开始就建立了一个静态对象,所以线程天生安全。
既然饿汉式这么简洁,还线程天生安全,为什么要弄懂懒汉式呢?

因为
面试考的
基本上都是懒汉式

懒汉式为什么线程不安全?

class Singleton    {    private Singleton(){}         private static Singleton single;       public static Singleton get()            {    //1.当有例如A、B、C等多个线程同时进入时       if (single == null)      {        //--》A        //--》B        //--》C        single =new Singleton();        //2.则会建立多次对象    }     return single;    }    }    public class person    {    public static void main(String[] args)    {        Singleton f = Singleton.get();      }    }

建立多次对象,这是单例类构造中不被允许的。所以存在线程安全问题。
线程安全用同步解决,但如果简单的同步,会使效率变低很多,ex:

class Single    {    private static  Single f = null;    private Single(){};    public static Single get()    {        //每一次线程进入,都要对是否持有锁进行判断,效率偏低            synchronized(Single.class)            {            if(f==null)            f = new Single();            }        return f;    }    }    public class Demo {    public static void main(String[] args) {        Single f =Single.get();    }    }

就如代码注释中所写,不论时间先后,每一个线程试图进入时,都要对是否持有锁进行判断,效率偏低。

PS。锁就是锁旗标,也有叫“监视器”的,在我看的视频教学中,讲师将其称之为,很形象,就这么叫了。具体请见我的另一片文章 java.关于线程同步 。

所以就有了双重判断的格式用来提高效率:

    class Single    {    private static  Single f = null;    private Single(){};    //4.后续的C线程进入后    public static Single get()    {        //4.C线程将在这里被拦截,无法进入        //1.如果有A B两个线程同时进入了        if(f==null)             //2.--》A            // 2.--》B            synchronized(Single.class)            {            //3.只有一个线程能获取锁            //3.--》A            if(f==null)            //3.当A线程执行完之后,f就不为空了,当之后的B进入的时候,即使B持有锁,也不会进行对象的建立了            f = new Single();            }        return f;    }    }    public class Demo {    public static void main(String[] args) {        Single f =Single.get();    }    }

双重判定使第一个进入的线程执行完之后,两个if判断都将返还false,之后尝试进入的线程在if那一关就会停下,不必每个线程都要判断是否持有锁,在一定程度上提高的效率。

最后附加饿汉式线程安全的几个简单的问题
1.懒汉式和饿汉式的区别:懒汉式存在延迟加载
2.懒汉式的延迟加载有没有问题:有,如果多线程访问,会存在线程的安全问题
3.怎么解决:用同步解决,但用一些同步代码块和同步函数都稍微有些低效,用双重判定的形式,能在一定程度上解决低效问题。
4.加同步的时候,用的锁是哪一个:该类所属的字节码文件对象

可能写的不够形象精确。。。。但渣渣已经尽力了。如果有路过的大神发现哪个地方写的不对,还望指正。

以上。

0 0
原创粉丝点击