扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
目录
我们一直强调成都网站建设、网站设计对于企业的重要性,如果您也觉得重要,那么就需要我们慎重对待,选择一个安全靠谱的网站建设公司,企业网站我们建议是要么不做,要么就做好,让网站能真正成为企业发展过程中的有力推手。专业网站制作公司不一定是大公司,创新互联作为专业的网络公司选择我们就是放心。前言:
单线程下的单例模式
饿汉模式
懒汉模式
多线程下的单例模式
懒汉模式的修改
1.加锁
2.有条件的加锁
3.解决内存可见性和指令重排序
本片文章介绍设计模式中的一个模式——单例模式。
单例模式就是只允许创建出一个实例的类。比如之前在使用JDBC编程的时候的DataSource这个类,就可以使用单例模式。见这篇文章http://t.csdn.cn/uq1lR。
其中单例模式有很多种实现方法,这里只用懒汉模式和饿汉模式来实现单例模式。
单线程中,没有线程安全问题,其代码会简单很多。
饿汉模式饿汉模式体现出一个字——急。因为这个实例直接就是在类加载阶段就被创建出来。
class SingletonHungry{
// 用static修饰,这样在类加载阶段就有了这个实例
private static SingletonHungry instance = new SingletonHungry();
// 用静态公开方法返回实例
public static SingletonHungry getInstance() {
return instance;
}
// 为了防止实例化多个对象,把构造方法用private修饰
private SingletonHungry(){};
}
public class SingletonTest1 {
public static void main(String[] args) {
SingletonHungry instance1 = SingletonHungry.getInstance();
SingletonHungry instance2 = SingletonHungry.getInstance();
System.out.println(instance1 == instance2);
//SingletonHungry instance3 = new SingletonHungry();
}
}
懒汉模式突出一个字——懒。这个实例是只有调用获取实例方法的时候才创建出来。
class SingletonLazy{
private static SingletonLazy instance = null;
// 只有在调用了该方法后才创造出了实例
public static SingletonLazy getInstance() {
// 如果已经创建了该实例,就直接返回之前的实例
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
// 为了防止实例化多个对象,把构造方法用private修饰
private SingletonLazy(){}
}
public class SingletonTest2 {
public static void main(String[] args) {
SingletonLazy instance1 = SingletonLazy.getInstance();
SingletonLazy instance2 = SingletonLazy.getInstance();
System.out.println(instance1 == instance2);
//SingletonLazy instance3 = new SingletonLazy();
}
}
代码结果如上。
在上述代码中,如果考虑多线程的话
饿汉模式没有线程安全问题,它直接就是在类加载是创建出了一个实例,使用该实例也没有其他修改的操作,直接返回即可。
懒汉模式是有线程安全问题。它又要判断比较实例是否为null,又要创建一个实例,最后才返回实例。其中对于实例是由修改的操作的。只要有修改操作,就可能会有线程安全问题。如下图:
懒汉模式的修改 1.加锁对于这种又有读,又有写的操作,保持原子性——加锁即可。
public SingletonThread getInstance() {
// 这里对于load cmp new这几步加锁,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
return instance;
}
2.有条件的加锁但是加锁是一种开销比较大的操作,上述加锁操作并不是每次都要加锁的。如果已经创建了实例,直接返回实例即可。
public SingletonThread getInstance() {
// 如果实例未被创建,用加锁的方法创建实例
// 如果创建,直接返回
if (instance == null) {
// 这里对于load cmp new这几步加锁,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
}
return instance;
}
3.解决内存可见性和指令重排序内存可见性:因为instance要读取并修改,所以对于内存可见性的问题也要预防。
指令重排序:
上面的new实例的指令又分为三个顺序步骤:
①申请内存空间
②调用构造方法,实例化一个对象
③把内存空间的地址赋值给这个对象
这几步可能可能会变成①③②。单线程下没有问题,但是多线程就会有问题了。
要想解决这两个问题,使用volatile关键字修饰instance即可。
private volatile static SingletonThread instance = null;
完整的懒汉模式的线程安全代码如下:
// 修改懒汉模式,使其线程安全
class SingletonThread {
// 使用volatile解决内存可见性和指令重排序问题
private volatile static SingletonThread instance = null;
public SingletonThread getInstance() {
// 如果实例未被创建,用加锁的方法创建实例
// 如果创建,直接返回
if (instance == null) {
// 这里对于load cmp new这几步加锁,保持了其原子性
synchronized (SingletonThread.class) {
if (instance == null) {
instance = new SingletonThread();
}
}
}
return instance;
}
}
有什么问题评论区指出。希望可以帮到你。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流