Java线程和多线程——单例类中的线程安全
Posted EthanPark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java线程和多线程——单例类中的线程安全相关的知识,希望对你有一定的参考价值。
单例模式是最广泛使用的创建模式之一。在现实世界之中,诸如Databae
的连接或者是企业信息系统(EIS)等,通常其创建都是受到限制的,应该尽量复用已存在对象而不是频繁创建销毁。为了达到这个目的,开发者通常会通过实现单例模式来创建一个wrapper类,来封装资源,限制其运行时所创建对象的个数。
单例中的线程安全
总的来说,开发者一般会按照如下的方式来创建单例的类:
- 使用私有构造函数来避免其它外部引用通过new的方式来创建新的对象引用。
- 声明一个该类的私有静态变量为实例。
- 提供一个公有的静态方法来返回单例的实例。如果实例还没有初始化的话,就将其初始化后再返回实例。
通过上面的步骤,我写了一个如下的单例类:
package com.sapphire.designpatterns;
public class ASingleton
private static ASingleton instance = null;
private ASingleton()
public static ASingleton getInstance()
if (instance == null)
instance = new ASingleton();
return instance;
在上面的代码中,getInstance()
方法不是线程安全的。多线程可以在同一时间访问这个方法,而在最开始的少数线程中,实例没有初始化的时候,多线程可以进入到if代码块来创建多个实例,就破坏了单例模式。
通常来说,有三种方式来让我们保证单例模式的线程安全。
在class加载的时候就创建实例变量
这种实现方式有如下优点:
- 不需要同步即可实现线程安全
- 容易实现
但是也有一些缺点:
- 过早的创建了资源,但是应用可能并不会使用这个资源,造成了资源浪费
- 调用方式无法传入任何参数的,所以我们无法复用这个类。举例来说,如果我们希望有一个单例类能够处理所有的数据库连接信息,并希望能够传入一些数据库信息的话,我们就无法复用这个单例类了。
同步getInstance()
方法
这种方法有如下优点:
- 保证了线程安全
- 调用方可以传入任何参数
- 可以保证延迟初始化
当然这种方法也有一些缺点:
- 因为使用了同步方法,会锁定资源,令所有客户端的请求会优先请求锁,从而降低了性能
- 包含了很多非必要的同步,因为一旦实例创建完成,同步就只是浪费了性能而没有任何作用了
在if代码块中使用同步代码块
优点:
- 保证了线程的安全
- 调用方可以传递参数
- 保证了延迟初始化
- 最小化了同步负载,仅仅在最开始的几个线程请求的时候进行同步操作
淡然也有一定的缺点
- 需要额外的if条件判断
从三种方法来判断,第三种方式应该是最佳的方式来实现同步了,代码大体如下:
package com.sapphire.designpatterns;
public class ASingleton
private static ASingleton instance= null;
private static Object mutex= new Object();
private ASingleton()
public static ASingleton getInstance()
if(instance==null)
synchronized (mutex)
if(instance==null) instance= new ASingleton();
return instance;
注意,
String
对象非常不适合来作为同步的锁,因为String
可能是复用的对象,锁定String很容易产生死锁切很难发现,所以这里使用的是Object
对象来作为同步锁。
以上是关于Java线程和多线程——单例类中的线程安全的主要内容,如果未能解决你的问题,请参考以下文章