JAVA单例模式

来源:互联网 发布:linux signal函数 编辑:程序博客网 时间:2024/05/17 10:43
package test3;


import java.util.HashMap;
import java.util.Map;


/**
 * 几种单例模式创建 
 * @author lv
 */
//满足的条件如下:
//1.私有化静态对象,加载时不做初始化操作
//2.私有化空的构造方法,避免外部创建实例
//3.创建一个获取该对象唯一实例的静态工厂方法


/**
 * 问题:
 * 1、为什么饿汉模式是线程安全的 ?因为类的加载机制
 *
 */
public class TestSingleton {
/*
*  1.懒汉模式(非线程安全的),在类加载的时候不创建单例实例,只有在第一次请求实例的时候才会创建,
*  并且只会在第一次请求后创建,之后不再创建。
*  优势:在需要时加载,减少了资源开销  lazy loading
*  劣势:加载效率低下,并且多线程情况下不安全。  
  
private static TestSingleton instance;
private  TestSingleton(){
}
public static TestSingleton getInstance(){
if(instance == null)
instance = new TestSingleton();
return instance;
}

*/


/*
*  2.懒汉模式(线程安全的),在类加载的时候不创建单例实例,只有在  第一次请求实例的时候  才会创建,
*  并且只会在第一次请求后创建,之后不再创建。
*  优势:在需要时加载,减少了资源开销  lazy loading,多线程情况下安全。
*  劣势:加载效率非常低下,并且多数情况下不需要线程同步。
*  方式一:getInstance()方法上加同步锁。
 
private static TestSingleton instance = null;
private  TestSingleton(){
}
public static synchronized TestSingleton getInstance(){
if(instance == null)
instance = new TestSingleton();
return instance;
}

*/

//测试用
private String name;
private static int i= 0; // 测试有几个实例,即是否是单例模式
public void setName(String str){
name = str;
}
public synchronized void printf(){
System.out.println("实例名:"+name+i);
}
/** 注意区分下面这个
public void printf(){
System.out.println("实例名:"+name+i);
}
*/

/*
*  方式二:双重校验锁。
*  做了两次null检查,确保只有第一次调用单例的时候才会做同步,同时避免了每次都同步的性能损耗
*/
private volatile static TestSingleton instance = null;   //注意  volatile
private TestSingleton(){
}
public static TestSingleton getInstance(){
if(instance == null){
synchronized (TestSingleton.class) {
if(instance == null){
instance = new TestSingleton();
i++;// 测试有几个实例,即是否是单例模式
}
}
}
return instance;
}


/**
* volatile 用法:
* 用volatile修饰的变量,线程在每次使用变量前,都会从共享空间读取最新的变量值,该值却不一定是正确的值。
* volatile很容易被误用来进行 原子性  操作。具体请自查。

*/


/*
*  方式三:静态内部类。
*  利用classloader的机制来保证初始化instance时只有一个线程,并且只有在调用getInstance()方法时才会
*  加载内部类LazyHolder进而实例化instance,既保证线程安全有没有太多性能损耗达到 lazy loading的效果。
*  

private static class LazyHolder{
private static final TestSingleton INSTANCE = new TestSingleton();
}
private TestSingleton(){
}
public static final TestSingleton getInstance(){
return LazyHolder.INSTANCE;
}

*/


/*
*  方式四:枚举。
* 这种方式是Effective Java作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止
* 反系列化重新创建新的对象
*  

public enum TestSingleton{
INSTANCE;
public void whateverMethod(){

}
}

*/



/*
*  3.饿汉模式(永远是线程安全的),基于classloader机制避免了多线程同步的问题,
*  在类加载的时候  唯一实例已经被创建。
*  在类加载的时候就实例化,会增加不必要资源开销
*  

private static final TestSingleton INSTANCE = new TestSingleton();//注意 final
private TestSingleton(){
}
public static TestSingleton getInstance(){
return INSTANCE;
}

*/


/*
*  4.饿汉模式(变种)
*  

private static TestSingleton instance = null;
private TestSingleton(){
}
static{
instance = new TestSingleton();
}
public static TestSingleton getInstance(){
return instance;
}

*/


/*
*  5.登记式单例
*  这个单例实际上维护的是一组单例类的实例,将这些实例存放在一个Map(登记簿)中,对于已经
*  登记过的实例,则从工厂直接返回,对于没有登记的,则先登记,而后返回。
*  其内部实现用的是饿汉模式,因为 static 语句块 在类被加载的时候就已被实例化。
 
// 登记簿,用来存放所有登记的实例
private static Map<String,TestSingleton> map = new HashMap<String, TestSingleton>();
//受保护的默认构造方法
protected TestSingleton(){
}
//在类加载的时候添加一个实例到登记簿
static{
TestSingleton singleton = new TestSingleton();
map.put(singleton.getClass().getName(), singleton);
}
//静态工厂方法,返回指定登记对象的唯一实例,对于已登记的直接取出返回,对于还未登记的,先登记,
//然后取出返回。
public static TestSingleton getInstance(String name){
if(name == null){
name = "TestSingleton";//TestSingleton.class.getName()
}
if(map.get(name) == null){
try{
map.put(name, (TestSingleton)Class.forName(name).newInstance());
}catch(InstantiationException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
return map.get(name);
}

*/



/**
* 两个问题要注意:
* 1、如果单例由不同的类加载器加载,那就有可能存在多个单例类的实例。
* 假定不是远端存取,例如一些servlet 容器对每个servlet 使用完全不同的 类加载器 ,这样的话如果有两个
* servlet 访问一个单例类,它们就都会有各自的实例。
*/
//解决办法
private static Class getClass(String classname) throws ClassNotFoundException{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null){
classLoader = TestSingleton.class.getClassLoader();
}
return (classLoader.loadClass(classname));
}

/**
* 2、如果TestSingleton实现了 java.io.Serializable 接口,那么这个类的实例就可能被序列化复原。
* 不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
*/
// 解决办法
/**

 
public class TestSingleton implements java.io.Serializable{
public static final TestSingleton INSTANCE =  new TestSingleton();
protected TestSingleton(){
}
private Object readResolve(){
return INSTANCE;
}
}

*/
//一般情况下,我会使用 饿汉模式,只有在要明确实现  lazy loading 效果时才会使用 内部类 的方式
//如果涉及到反序列化创建对象时 使用 枚举

//如果有其它特殊要求,可能会用 双层校验锁的方式。

}




/**

* 测试类

*/

package test3;


public class TestTestSingleton{


/**
* @param args
*/
public static void main(String[] args) {
//创建任务
Runnable runn1 = new MyRunnable1();
Runnable runn2 = new MyRunnable2();

//创建线程,并将任务指派给线程
Thread t1 = new Thread(runn1);
Thread t2 = new Thread(runn2);

t1.start();
t2.start();


}
}
class MyRunnable1 implements Runnable{
public void run(){
// TestSingleton t = TestSingleton.getInstance(null);
TestSingleton t = TestSingleton.getInstance();
t.setName("1");
t.printf();
}
}
class MyRunnable2 implements Runnable{
public void run(){
// TestSingleton t = TestSingleton.getInstance(null);
TestSingleton t = TestSingleton.getInstance();
t.setName("2");
t.printf();
}
}


原创粉丝点击