设计模式——单例模式(Java)——考虑多线程环境下的线程安全问题

设计模式——单例模式(Java)——考虑多线程环境下的线程安全问题

一:单例模式概念

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例

二:单例模式的实现方式

特别注意,在多线程环境下,需要对获取对象实例的方法加对象锁(synchronized)

方式一:(懒汉式)程序执行过程中需要这个类的对象,再实例化这个类的对象

步骤:

1.定义静态私有对象

2.构造方法私有化保证在类的外部无法实例化该类的对象

3.定义对外开放的静态方法在调用方法时判断对象是否为空,为空再创建对象返回


public class Singleton{

//1.定义静态私有对象

priavate static Singleton singleton;

//2.构造方法私有化,保证在类的外部无法实例化该类的对象

private Singleton(){

}

//3.定义对外开放的静态方法在调用方法是判断对象是否为空,为空再创建对象返回

public static synchronized Singleton getSingletonInstance(){

if(singleton==null){

singleton=new Singleton();

}

return singleton;

}

}

方式二:(饿汉式)类加载的时候就实例化该类的对象

步骤:

1.定义静态私有对象,并实例化

2.构造方法私有化保证在类的外部无法实例化该类的对象

3.定义对外开放的静态方法


public class Singleton{

 //1.定义静态私有对象,并实例化

 priavate static Singleton singleton=new Singleton();

 //2.构造方法私有化,保证在类的外部无法实例化该类的对象

 private Singleton(){

 }

 //3.定义对外开放的静态方法

 public static synchronized Singleton getSingletonInstance(){

 return singleton;

 }

}

三:在多线程情况下,单例对象的同步问题

1.单例对象的初始化

1.1问题引入:

在多线程情况下,如果第一个线程发现对象为空,准备创建;这时第二个线程同时也发现对象为空,也会创建。这样就会造成在一个JVM中有多个单例类型的实例。

1.2问题解决:(对象的创建加同步锁sychronized)


 1 //单例对象的初始化同步
 2 public class GlobalConfig {
 3     private static GlobalConfig instance = null;
 4     private Vector properties = null;
 5     private GlobalConfig() {
 6       //Load configuration information from DB or file
 7       //Set values for properties
 8     }
 9     private static synchronized void syncInit() {
10       if (instance == null) {
11         instance = new GlobalConfig();
12       }
13     }
14     public static GlobalConfig getInstance() {
15       if (instance == null) {
16         syncInit();
17       }
18       return instance;
19     }
20     public Vector getProperties() {
21       return properties;
22     }
23   }

  这种处理方式虽然引入了同步代码,但是因为这段同步代码只会在最开始的时候执行一次或多次,所以对整个系统的性能不会有影响。

2.单例对象的属性更新

2.1问题引入:

当一个线程更新单例对象的属性时,如果有另一个线程正在读取,这样就会造成属性值的不一致问题

2.2问题解决:

参照读者/写者的处理方式,设置一个读计数器,每次读取配置信息前,将计数器加1,读完后将计数器减1.只有在读计数器为0时,才能更新数据,同时要阻塞所有读属性的调用。

public class GlobalConfig {
 2     private static GlobalConfig instance = null;
 3     private Vector properties = null;
 4     private GlobalConfig() {
 5       //Load configuration information from DB or file
 6       //Set values for properties
 7     }
 8     private static synchronized void syncInit() {
 9       if (instance = null) {
10         instance = new GlobalConfig();
11       }
12     }
13     public static GlobalConfig getInstance() {
14       if (instance = null) {
15         syncInit();
16       }
17       return instance;
18     }
19     public Vector getProperties() {
20       return properties;
21     }
22     public void updateProperties() {
23       //Load updated configuration information by new a GlobalConfig object
24       GlobalConfig shadow = new GlobalConfig();
25       properties = shadow.getProperties();
26     }
27   }

参考资料:

1.

Java单例模式深入详解

http://www.cnblogs.com/hxsyl/archive/2013/03/19/2969489.html

2.

单例模式多线程问题:

http://zhidao.baidu.com/question/2116683855745036107.html

时间: 2024-10-10 10:10:03

设计模式——单例模式(Java)——考虑多线程环境下的线程安全问题的相关文章

多线程环境下的线程不安全问题(1)

在不考虑多线程的情况下,很多类代码都是完全正确的,但是如果放在多线程环境下,这些代码就很容易出错,我们称这些类为 线程不安全类 .多线程环境下使用线程安全类 才是安全的. 下面是一个线程不安全类的例子: public class Account { private Integer balance; public Account(Integer balance) { super(); this. balance = balance; } public Integer getBalance() {

多线程环境下的线程不安全问题(2)

body { font-family: 微软雅黑,"Microsoft YaHei", Georgia,Helvetica,Arial,sans-serif,宋体, PMingLiU,serif; font-size: 10.5pt; line-height: 1.5; } html, body { } h1 { font-size:1.5em; font-weight:bold; } h2 { font-size:1.4em; font-weight:bold; } h3 { fon

Java读写锁,多线程环境下提升效率

读写锁 package cn.sniper.thread.lock; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class Cache {

单例模式在多线程环境下的lazy模式为什么要加两个if(instance==null)

刚才在看阿寻的博客”C#设计模式学习笔记-单例模式“时,发现了评论里有几个人在问单例模式在多线程环境下为什么lazy模式要加两个if进行判断,评论中的一个哥们剑过不留痕,给他们写了一个demo来告诉他们为什么. 我看了一下这个demo,确实说明了这个问题,但我认为不够直观,呵呵,于是我就稍微的改了一下. 这是剑过不留痕的demo using System; using System.Threading; namespace SingletonPattern { class Program { s

设计模式 -- 单例模式(Java&&PHP)

所谓单例模式,简单来说,就是在整个应用中保证只有一个类的实例存在.就像是Java Web中的application,也就是提供了一个全局变量,用处相当广泛,比如保存全局数据,实现全局性的操作等. 能够想到的最简单的实现是,把类的构造函数写成private的,从而保证别的类不能实例化此类,然后在类中提供一个静态的实例并能够返回给使用者.这样,使用者就可以通过这个引用使用到这个类的实例了. 单例模式在这里介绍两种,一种是饿汉,一种是懒汉. 饿汉单例模式                        

HttpClient在多线程环境下踩坑总结

问题现场 在多线程环境下使用HttpClient组件对某个HTTP服务发起请求,运行一段时间之后发现客户端主机CPU利用率呈现出下降趋势,而不是一个稳定的状态. 而且,从程序日志中判断有线程处于夯住的状态,应该是被阻塞了. 问题排查 一开始找不到原因,怀疑是多线程并发导致的死锁问题,但是通过代码审查并未定位到任何可能的多线程并发问题. 甚至开始怀疑是否是因为内存资源不够引起JVM频繁GC到导致业务线程被暂停,但是从GC的日志输出结果看,GC是正常的. 于是,进入一种丈二和尚摸不着头脑头脑的状态,

SQLite在多线程环境下的应用

这几天研究了一下SQLite这个嵌入式数据库在多线程环境下的应用,感觉里面的学问还挺多,于是就在此分享一下. AD: 2014WOT全球软件技术峰会北京站 课程视频发布 先说下初衷吧,实际上我经常看到有人抱怨SQLite不支持多线程.而在iOS开发时,为了不阻塞主线程,数据库访问必须移到子线程中.为了解决这个矛盾,很有必要对此一探究竟. 关于这个问题,最权威的解答当然是SQLite官网上的“Is SQLite threadsafe?”这个问答. 简单来说,从3.3.1版本开始,它就是线程安全的了

C++多线程环境下的构造函数

多线程的环境里,我们总不可避免要使用锁.于是一个常见的场景就是: 1 class ObjectWithLock 2 { 3 private: 4 std::mutex mtx_; 5 SomeResType shared_res_; 6 7 public: 8 // Constructor/Destructor 9 … 10 11 void OpOnSharedRes() 12 { 13 std::lock_guard<std::mutex> lock(mtx_); 14 15 // read

UNIX多线程环境下屏障功能(barrier)浅析

说起屏障这个东西,相信对于大多数朋友来说比较陌生,不过要是说起pthread_join这个函数,相信都比较熟悉.我们通常使用这个函数来等待其它线程结束,例如主线程创建一些线程,这些线程去完成一些工作,而主线程需要去等待这些线程结束.其实pthread_join就实现了一种屏障.我们可以对屏障这样理解,把屏障理解为为了协同线程之间的工作而使得某一具体线程进入等待状态的一种机制.下面我们来看看UNIX为我们提供的一个屏障——barrier. 注:与所有的线程函数一样,使用屏障barrier时需要包含