多线程之单例模式
饿汉式:(相对较于安全)1
2
3
4
5
6
7
8
9class 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
24public 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来实现线程安全。
其实使用静态初始化器的方式会在类加载时创建类的实例,但是我们将实例的创建显式放置在静态内部类中,它会导致在外部类加载时不进行实例创建,这样就能实现我们的双重目的:延迟加载和线程安全。