Java中的单例模式
单例模式就是确保一个类在程序中只存在一个实例并且提供这个实例对象
实现单例模式的方法:饿汉式和懒汉式
1、饿汉式
饿汉式是实现单例的最简单的一直方式,在类加载过程中实例化对象 多线程环境下不会出现问题
具体代码实现:
// 饿汉式
public class EagerSingleton {
// 必须使用static修饰 因为单例模式第一次只能直接通过类名去访问获取实例对象的方法
// 而且饿汉式中要在类加载的过程中实例化对象
//final关键字 为了防止实例对象被修改
private static final EagerSingleton INSTANCE = new EagerSingleton();
EagerSingleton(){
}
public static EagerSingleton getInstance(){
return INSTANCE;
}
}
饿汉式单例缺点也很明显 因为在类加载的时候就创建了实例对象,如果在整个程序中没有使用到实例对象则就浪费了空间,而且一旦代码写定构造实例的方法也定了无法支持定制化的对象实例,无法选择使用有参还是无参构造方法
2、懒汉式
为了解决饿汉式浪费空间的问题,懒汉式单例不会在类加载的时候创建单例,会在需要的时候才创建
1.单线程环境下安全的懒汉式(最简版)
// 单线程环境下安全懒汉式单例
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(instance==null){
instance = new LazySingleton();
}
return instance;
}
}
多线程环境下进行测试:
测试代码:
public class T {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { //开启十条线程进行测试
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(Thread.currentThread().getName()+"号线程----->"+instance);
},Integer.toString(i)).start();
}
}
}
测试结果:
很明显在多线程环境下会出现问题,就是因为多个线程同时进入了If条件句中导致重复实例化对象
2.在方法声明上使用synchronized 同步整个方法(效率极低)
// 在方法声明上使用synchronized 懒汉式单例
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){
}
public synchronized static LazySingleton getInstance(){
if(instance==null){
instance = new LazySingleton();
}
return instance;
}
}
测试结果:
这种方法在多线程环境下虽然不会出现问题 但是效率太低了
3.同步代码块(依旧会出现线程安全问题)
// 在方法块上使用synchronized 多线程环境下依然会出现问题
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(instance==null){
synchronized (LazySingleton.class){
instance = new LazySingleton();
}
}
return instance;
}
}
测试结果:
会出现和不使用synchronized关键字时一样的问题
多个线程同时进入到if代码块之后 在拿到琐之后依然会重复执行实例化对象的操作
4.在同步代码块中再加一个if条件判断 双重检查
// 同步代码块并且进行双重检查
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(instance==null){
synchronized (LazySingleton.class){
if(instance==null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
测试结果:
多线程环境下基本不会出现问题,但是这依然不是最终版本 instance = new LazySingleton();这行语句可能由于指令重排导致程序出现问题
5.同步代码块,双重检查并且禁止指令重排(最终版)
// 同步代码块,双重检查并且禁止指令重排
public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(instance==null){
synchronized (LazySingleton.class){
if(instance==null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
虽然这种 同步代码块,双重检查并且禁止指令重排的懒汉式单例在多线程环境下安全 但是并不是绝对的安全在Java中可以使用反射来破解所以这种单例也不是绝对的安全
6.使用枚举类
反射无法破解枚举类
public enum EnumSingleton {
INSTANCE;
public void method(){
//业务方法1
System.out.println("业务方法1");
}
public void method2(){
//业务方法2
System.out.println("业务方法2");
}
}