多线程之单例模式

多线程之单例模式

饱汉式与饿汉式

饿汉式:(相对较于安全)

1
2
3
4
5
6
7
8
9
class Single
{
private static final Single SINGLE_INTSTANCE = new Single();
private Single(){}
public static Single getInstance()//函数内只有一个return,否则多线程可能会有安全问题。
{
return SINGLE_INTSTANCE;
}
}

饱汉式:延迟加载模式(在多线程并发访问时,会出现线程安全问题。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
s = new Single();

}
return s;
}
}

*安全问题://假设线程1判断完为null后进入if代码块却同时转换为线程2,则可能线程2new对象,使对象new多了。


优化代码第一步

1
2
3
4
5
6
7
8
9
10
11
12
13
 class Single
{
private static Single s = null;
private Single(){}
public static synchronized Single getInstance()
{
if(s==null)
{
s = new Single();
}
return s;
}
}

优化细节:加入synchronized可以保证不会发生上述的安全问题。
代码缺陷:线程每次都要判断有没有锁,然后再判断if,如此往复会降低效率。


优化代码第二步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronize(Sing.class)
{
if(s==null)
{
s = new Single();
}
}
}
return s;
}
}

优化细节:转化成同步代码块,同时加入了两次if判断。
当对象创建以后,线程就只需要进行一次if判断就可以结束了。

—-优化代码第三步

类级内部类方式

  饿汉式会占用较多的空间,因为其在类加载时就会完成实例化,而懒汉式又存在执行速率慢的情况,双重加锁机制呢?又有执行效率差的毛病,有没有一种完美的方式可以规避这些毛病呢?

  貌似有的,就是使用类级内部类结合多线程默认同步锁,同时实现延迟加载和线程安全。

单例模式最优方案
线程安全 并且效率高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SingletonTest { 

// 定义一个私有构造方法
private SingletonTest() {

}
//定义一个静态私有变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用)
private static volatile SingletonTest instance;

//定义一个共有的静态方法,返回该类型实例
public static SingletonTest getIstance() {
// 对象实例化时与否判断(不使用同步代码块,instance不等于null时,直接返回对象,提高运行效率)
if (instance == null) {
//同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建)
synchronized (SingletonTest.class) {
//未初始化,则初始instance变量
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}

  如上代码,所谓类级内部类,就是静态内部类,这种内部类与其外部类之间并没有从属关系,加载外部类的时候,并不会同时加载其静态内部类,只有在发生调用的时候才会进行加载,加载的时候就会创建单例实例并返回,有效实现了懒加载(延迟加载),至于同步问题,我们采用和饿汉式同样的静态初始化器的方式,借助JVM来实现线程安全。

  其实使用静态初始化器的方式会在类加载时创建类的实例,但是我们将实例的创建显式放置在静态内部类中,它会导致在外部类加载时不进行实例创建,这样就能实现我们的双重目的:延迟加载和线程安全。