您的位置:首页 > 编程语言 > Java开发

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

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

先小小的介绍一下,单例类是什么

单例类是一种设计思想

简单的基本思想:私有化结构体,通过判断建立的实例是否为空来判断是否能创建对象。使获取对象的途径不是经过结构体,而是通过定义的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.加同步的时候,用的锁是哪一个:该类所属的字节码文件对象

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

以上。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: