java基础之单例模式

来源:互联网 发布:新材料在线软件 编辑:程序博客网 时间:2024/06/06 12:22

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

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

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();    }}
原创粉丝点击