1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
java中两种类型单例模式:
多线程安全是指:当多个线程同时访问某个方法时,资源按照一定的规律分配给不同的线程使用,在某个时间段只能有一个线程访问、并且上锁,只有当前线程释放了锁时,其他线程才能访问,保证了数据的正确性。
适用于单线程(多线程不安全):
//饿汉式单例类
public class Singleton {
//私有的默认构造子,外部无法实例化
private Singleton1() {}
//声明一个静态常量对象
private static final Singleton single = new Singleton();
//静态工厂方法
public static Singleton getInstance() {
return single;
}
}
适用于多线程(多线程安全):
//懒汉式单例类.在第一次调用的时候实例化
public class Singleton2 {
//私有的默认构造子
private Singleton2() {}
//注意,这里没有final
private static Singleton2 single=null;
//静态工厂方法 ,用了同步,保证线程的安全性
public static synchronized Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
//双重锁形式
public class Singleton{
private static volatile Singleton instance=null;
private Singleton(){
//do something
}
public static Singleton getInstance(){
if(instance==null){
synchronized(SingletonClass.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
//这个模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。
//这种模式中双重判断加同步的方式,比第一个例子中的效率大大提升,因为如果单层if判断,在服务器允许的情况下,
//假设有一百个线程,耗费的时间为100*(同步判断时间+if判断时间),而如果双重if判断,100的线程可以同时if判断,理论消耗的时间只有一个if判断的时间。
//所以如果面对高并发的情况,而且采用的是懒汉模式,最好的选择就是双重判断加同步的方式。
import java.util.HashMap;
import java.util.Map;
//登记式单例类.
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
//通过反射将类名放到map里面
map.put(single.getClass().getName(), single);
}
//保护的默认构造子
protected Singleton3(){}
//静态工厂方法,返还此类惟一的实例
public static Singleton3 getInstance(String name) {
//如果在调用单例方法的时候没给出类名称
if(name == null) {
//那么通过反射去取得类名称
name = Singleton3.class.getName();
System.out.println("name == null"+"--->name="+name);
}
//得到类名称后,但是map里面没有存该类名的键值对
if(map.get(name) == null) {
try {
//通过反射去给该类实例化一下对象并且放到map里面
map.put(name, (Singleton3) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//最后通过以上操作确保了有该类名称的键值对存在,直接去map里面取值即可
return map.get(name);
}
//一个示意性的商业方法
public String about() {
return "Hello, I am RegSingleton.";
}
public static void main(String[] args) {
Singleton3 single3 = Singleton3.getInstance(null);
System.out.println(single3.about());
}
}