java基础之单例模式

首先单例模式就是说一个类的对象从始至终只有一个,即使再去申请该类的时候,也还是会返回给程序上次已经返回的对象。

单例模式的时候需要注意有两种方法,懒汉式(真正使用的时候才会初始化该对象)和饿汉式(类在加载的时候就会初始化该对象),但是在使用懒汉式的时候就需要注意线同步的问题。

1.懒汉式
普通懒汉式
public Class Singleton {
    private static Singleton single = null;
    private Singleton();    // 构造方法为私有化,禁止其他类初始化

    public static Singleton getInstance() {
        if(single == null) {
            single = new Singleton();
        }
        return single;
    }
}

这样我们在需要使用该类的对象的时候就直接调用getInstance方法就可以获取到了。

但是有个问题,懒汉式在多线程情况下回出现两个线程先后进入,然后造成对象多次分配的情况。

举个例子,线程一先判断,由于single对象为空,因此开始初始化,但是还没有初始化的时候,线程二又进来判断,这样两个都是判断的为null,结果两个都进行了new,产生了两个对象。

这样就引入了一个新的,带锁的单例模式

加同步的懒汉模式

为了在多线程的情况下能正常工作,对于getInstance方法加上同步

public Class Singleton {
    private static Singleton single = null;
    private Singleton();    // 构造方法为私有化,禁止其他类初始化

    public static synchronized Singleton getInstance() {
        if(single == null) {
            single = new Singleton();
        }
        return single;
    }
}

这样在线程1进入getInstance方法的时候其他线程就不会进入该方法去进行判断对象是否为空,不会出现多线程的时候不同步的情况。

但同步的懒汉模式有一个性能问题,这样会导致每次进入getInstance都会同步。

双重锁检查
private volatile static Singleton single;
public static Singleton getInstance() {
    if(single == null) {
        synchronized(Singleton.class) {
            if(single == null) {
                single = new Singletoon();
            }
        }
    }
    return single;
}

只有在single没有被初始化的时候,才会进行同步初始化single对象。

注意这里对于single添加了修饰符volatile,这是为了防止在多线程的情况下出现问题。

2.饿汉模式

饿汉模式就是类在加载的时候就对single进行初始化了。

public class Singleton {
    private Singleton single = new Singleton();
    private Singleton();

    public Singleton getInstance() {
        return single;
    }
}

对于饿汉式的单例模式而言,天生就是线程安全的,但是在JVM加载类的时候就对单例类的对象进行了初始化,因此如果该对象只用一两次,并且使用时机比较靠后的话,饿汉式单例模式就会影响系统性能

但是懒汉式单例模式,只有在程序中需要使用该对象的时候才会进行初始化,这样就会节省系统资源。

3.实际项目中的一些技巧

前一段时间在项目中碰见了使用jedisPoold的场景,但是在使用JedisPool的时候可以获取到JedisPool的对象,但是在获取Jedis对象的时候就会报不能获取资源的异常。当时自己的代码是这样写的。

public class JedisUtil {

    private static JedisPool jedisPool = null;
    private static final JedisUtil util = new JedisUtil();

    private JedisUtil() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(-1);
        config.setMaxIdle(1000);

        try {
            String redisAddress = 获取redis地址
            jedisPool = new JedisPool(config, redisAddress, 6379);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static JedisUtil getInstance() {
        return util;
    }

    public static void set(String key, String value) {
        Jedis jedis = null;
        try {
            jedis = getConnect();
            jedis.set(key, value);
        } catch(JedisConnectionException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis != null) {
                jedis.close();
            }
        }
    }

    private static Jedis getConnect() {
        return jedisPool.getResource();
    }
}

但是由于redisAddress在传过来的时候可能每次不太一样,因此这样写的时候就不是一个单例模式了。

归根到底还是自己对于单例的理解不够,单例说的是一个资源在系统运行过程中只会有一个实例,在这个例子中的资源指的是jedisPool,而不是util,而util在实际使用过程中他可以理解为一个媒介,通过它去使用资源。

最后经过修改,这个工具类可以写为下面的样子

public class JedisUtils {

    private static final JedisUtils util = new JedisUtils();
    private static Map<String, JedisPool> jedisPools = new ConcurrentHashMap<>();

    private JedisUtils() {
    }

    public static JedisUtils getInstance() {
        return util;
    }

    public static void set(String key, String value, String redisAddress) {
        // 真正使用jedispool的时候再从里面获取
        // 没有的话先创建jedisPool
        if(!jedisPools.containsKey(redisAddress)) {
            generateJedisPool(redisAddress);
        }
        Jedis jedis = null;
        try {
            jedis = getConnect(redisAddress);
            jedis.set(key, value);
        } catch(JedisConnectionException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis != null) {
                jedis.close();
            }
        }
    }

    private static void generateJedisPool(String redisAddress) {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(-1);
        config.setMaxIdle(1000);

        try {
            JedisPool jedisPool = new JedisPool(config, redisAddress, 6379);
            jedisPools.put(redisAddress, jedisPool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Jedis getConnect(String redisAddress) {
        JedisPool jedisPool = jedisPools.get(redisAddress);
        return jedisPool.getResource();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值