单例模式

设计模式-单例模式

常见设计模式分三大类:

  1. 创建模式:
    工厂方法, 抽象工厂, 建造模式, 原型模式, 单例模式5种.
  2. 结构模式:
    适配器模式, 桥梁模式, 代理模式, 装饰模式, 门面模式5种.
  3. 行为模式:
    模板模式, 迭代器模式, 观察者模式, 状态模式, 策略模式5种.

单例模式

应用场景: 实际应用场景读取配置属性,单例模式保持虚拟机中只有一个实例.不是什么场景都适合使用单例模式的,保存配置文件信息算是典型的一个了.
特点:

  1. 只有一个实例;
  2. 自己创建实例;
  3. 必须给其他对象提供这一实例.

懒汉式(用的时候才实例化)

示例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Created by xiehui1956(@)gmail.com on 16-12-8.
*/
public class Singleton {

/**
* 构造函数私有,避免外部实例化
*/
private Singleton(){}

/**
* 声明实例
*/
private static Singleton singleton = null;

/**
* 实例化, 线程不安全
* @return
*/
public static Singleton getInstance(){
if (null == singleton)
singleton = new Singleton();
return singleton;
}

}

改进一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package comdata.synch.model;

/**
* Created by xiehui1956(@)gmail.com on 16-12-8.
*/
public class Singleton {

/**
* 构造函数私有,避免外部实例化
*/
private Singleton(){}

/**
* 声明实例
*/
private static Singleton singleton = null;

/**
* 实例化, 改进一. 效率低
* @return
*/
public static synchronized Singleton getInstance(){
if (null == singleton)
singleton = new Singleton();
return singleton;
}

}

改进二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package comdata.synch.model;

/**
* Created by xiehui1956(@)gmail.com on 16-12-8.
*/
public class Singleton {

/**
* 构造函数私有,避免外部实例化
*/
private Singleton() {
}

/**
* 声明实例
*/
private static Singleton singleton = null;

/**
* 实例化, 改进二. 双重检查锁定
*
* @return
*/
public static synchronized Singleton getInstance() {
if (null == singleton) {
synchronized (Singleton.class) {
if (null == singleton)
singleton = new Singleton();
}
}
return singleton;
}

}

该进三:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package comdata.synch.model;

/**
* Created by xiehui1956(@)gmail.com on 16-12-8.
*/
public class Singleton {

/**
* 构造函数私有,避免外部实例化
*/
private Singleton() {
}

/**
* 私有静态内部类构建实例,在使用的时候才会加载次类
*/
private static class LazyLoad {
private static final Singleton singleton = new Singleton();
}

/**
* 实例化, 改进三. 静态内部类
*
* @return
*/
public static final Singleton getInstance() {
return LazyLoad.singleton;
}

}

懒汉式(用不用都会实例化)

示例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package comdata.synch.model;

/**
* Created by xiehui1956(@)gmail.com on 16-12-8.
*/
public class Singleton {

/**
* 构造函数私有,避免外部实例化
*/
private Singleton() {
}

/**
* 实例化
*/
private static final Singleton singleton = new Singleton();

/**
* 对外入口
*
* @return
*/
public static Singleton getInstance() {
return singleton;
}

}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package comdata.synch;


import comdata.synch.model.Singleton;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Created by xiehui1956(@)gmail.com on 16-12-7.
*/
public class AppTest {

private Logger LOGGER = LoggerFactory.getLogger(AppTest.class);

@Test
public void testSingleton() {
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info(Singleton.getInstance().toString());
}
}).start();
}
}

}

------------------------------------
16:17:02.219 [Thread-0] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-6] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-5] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-2] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.219 [Thread-1] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-3] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.219 [Thread-4] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-8] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.224 [Thread-16] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.220 [Thread-7] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.221 [Thread-9] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.225 [Thread-17] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.221 [Thread-11] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83
16:17:02.221 [Thread-10] INFO comdata.synch.AppTest - comdata.synch.model.Singleton@51bf2a83

应用实例-读取properties配置

  1. PropertiesUtils.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package comdata.synch.util;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;

    /**
    * Created by xiehui1956(@)gmail.com on 16-11-8.
    */
    public class PropertiesUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesUtils.class);

    public static Properties loadProperties(String path) {
    Properties properties = new Properties();
    InputStream inputStream = null;
    try {
    inputStream = new FileInputStream(path);
    } catch (FileNotFoundException e) {
    LOGGER.error("读取配置时出错了,请注意配置路径config. 报错信息L{}", e.getStackTrace());
    }
    try {
    properties.load(inputStream);
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    if (null != inputStream)
    try {
    inputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    return properties;
    }
    }
  2. PropertiesConfig.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package comdata.synch.util;

    /**
    * Created by xiehui1956(@)gmail.com on 16-11-1.
    */
    public class PropertiesConfig {

    /**
    * cassandra
    */
    public static final String JDBC_CASSANDRA = "jdbc.cassandra.properties";

    /**
    * 应用信息配置
    */
    public static final String APP = "app.properties";

    /**
    * 数据库配置信息
    */
    public static final String JDBC = "jdbc.properties";

    /**
    * 获取配置文件路径
    *
    * @param fileName
    * @return
    */
    public static final String getConfigPath(String fileName) {
    return System.getProperty("config") + fileName;
    }

    }
  3. WebAppConfig.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    package comdata.synch;

    import comdata.synch.util.PropertiesConfig;
    import comdata.synch.util.PropertiesUtils;

    import java.util.Properties;

    /**
    * Created by xiehui1956(@)gmail.com on 16-11-8.
    * properties属性映射类
    */
    public final class WebAppConfig {

    private WebAppConfig() {
    System.setProperty("config", "/home/bls/workspace/work/project/data-sync/config/");
    Properties properties = PropertiesUtils.loadProperties(PropertiesConfig.getConfigPath(PropertiesConfig.APP));
    canalListenerIp = properties.getProperty("canal.listener.ip");
    canalListenerPort = Integer.parseInt(properties.getProperty("canal.listener.port"));
    canalListenerName = properties.getProperty("canal.listener.name");
    }

    private static class LazyLoad {
    private static final WebAppConfig appConfig = new WebAppConfig();
    }

    public static WebAppConfig getInstance() {
    return LazyLoad.appConfig;
    }

    /**
    * canal监听ipl
    */
    private String canalListenerIp;

    /**
    * canal监听port
    */
    private int canalListenerPort;

    /**
    * canal监听服务名
    */
    private String canalListenerName;

    public String getCanalListenerIp() {
    return canalListenerIp;
    }

    public void setCanalListenerIp(String canalListenerIp) {
    this.canalListenerIp = canalListenerIp;
    }

    public int getCanalListenerPort() {
    return canalListenerPort;
    }

    public void setCanalListenerPort(int canalListenerPort) {
    this.canalListenerPort = canalListenerPort;
    }

    public String getCanalListenerName() {
    return canalListenerName;
    }

    public void setCanalListenerName(String canalListenerName) {
    this.canalListenerName = canalListenerName;
    }
    }

    -----------------------------------
    输出结果: 16:37:17.194 [main] INFO com.data.synch.AppTest - 127.0.0.1

后来properties配置文件变多了,更新调整了以下类

  1. WebAppConfig.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    package comdata.synch;

    import comdata.synch.util.PropertiesConfig;
    import comdata.synch.util.PropertiesUtils;

    import java.util.Properties;

    /**
    * Created by xiehui1956(@)gmail.com on 16-11-8.
    * properties属性映射类
    */
    public final class WebAppConfig {

    private WebAppConfig() {
    setApp(new App());
    setSetup(new Setup());
    }

    private static class LazyLoad {
    private static final WebAppConfig appConfig = new WebAppConfig();
    }

    public static WebAppConfig getInstance() {
    return LazyLoad.appConfig;
    }

    private App app;
    private Setup setup;


    public App getApp() {
    return app;
    }

    public void setApp(App app) {
    this.app = app;
    }

    public Setup getSetup() {
    return setup;
    }

    public void setSetup(Setup setup) {
    this.setup = setup;
    }

    /**
    * 应用配置信息
    */
    public final class App {

    private App() {
    System.setProperty("config", "/home/bls/workspace/work/redstar/project/data-sync/config/");
    Properties properties = PropertiesUtils.loadProperties(PropertiesConfig.getConfigPath(PropertiesConfig.APP));
    canalListenerIp = properties.getProperty("canal.listener.ip");
    canalListenerPort = Integer.parseInt(properties.getProperty("canal.listener.port"));
    canalListenerName = properties.getProperty("canal.listener.name");
    }

    /**
    * canal监听ipl
    */
    private String canalListenerIp;

    /**
    * canal监听port
    */
    private int canalListenerPort;

    /**
    * canal监听服务名
    */
    private String canalListenerName;

    public String getCanalListenerIp() {
    return canalListenerIp;
    }

    public void setCanalListenerIp(String canalListenerIp) {
    this.canalListenerIp = canalListenerIp;
    }

    public int getCanalListenerPort() {
    return canalListenerPort;
    }

    public void setCanalListenerPort(int canalListenerPort) {
    this.canalListenerPort = canalListenerPort;
    }

    public String getCanalListenerName() {
    return canalListenerName;
    }

    public void setCanalListenerName(String canalListenerName) {
    this.canalListenerName = canalListenerName;
    }
    }

    /**
    * 启动信息
    */
    public final class Setup {

    private Setup() {
    System.setProperty("config", "/home/bls/workspace/work/redstar/project/data-sync/config/");
    Properties properties = PropertiesUtils.loadProperties(PropertiesConfig.getConfigPath(PropertiesConfig.APP));
    servicePort = Integer.parseInt(properties.getProperty("service.port"));
    redisMaxWaitMillis = Integer.parseInt(properties.getProperty("redis.MaxWaitMillis"));
    redisTimeout = Integer.parseInt(properties.getProperty("redis.timeout"));
    redisConfigServerUrl = properties.getProperty("redis.config.server.url");
    redisConfigServerPort = Integer.parseInt(properties.getProperty("redis.config.server.port"));
    redisClusterInfo = properties.getProperty("redis.cluster.info");
    }

    private int servicePort;
    private long redisMaxWaitMillis;
    private int redisTimeout;
    private String redisConfigServerUrl;
    private int redisConfigServerPort;
    //info realtime info
    private String redisClusterInfo;

    public int getServicePort() {
    return servicePort;
    }

    public void setServicePort(int servicePort) {
    this.servicePort = servicePort;
    }

    public long getRedisMaxWaitMillis() {
    return redisMaxWaitMillis;
    }

    public void setRedisMaxWaitMillis(long redisMaxWaitMillis) {
    this.redisMaxWaitMillis = redisMaxWaitMillis;
    }

    public int getRedisTimeout() {
    return redisTimeout;
    }

    public void setRedisTimeout(int redisTimeout) {
    this.redisTimeout = redisTimeout;
    }

    public String getRedisConfigServerUrl() {
    return redisConfigServerUrl;
    }

    public void setRedisConfigServerUrl(String redisConfigServerUrl) {
    this.redisConfigServerUrl = redisConfigServerUrl;
    }

    public int getRedisConfigServerPort() {
    return redisConfigServerPort;
    }

    public void setRedisConfigServerPort(int redisConfigServerPort) {
    this.redisConfigServerPort = redisConfigServerPort;
    }

    public String getRedisClusterInfo() {
    return redisClusterInfo;
    }

    public void setRedisClusterInfo(String redisClusterInfo) {
    this.redisClusterInfo = redisClusterInfo;
    }
    }


    }
  2. AppTest.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package comdata.synch;


    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    /**
    * Created by xiehui1956(@)gmail.com on 16-12-7.
    */
    public class AppTest {

    private Logger LOGGER = LoggerFactory.getLogger(AppTest.class);

    @Test
    public void testSingleton() {
    WebAppConfig appConfig = WebAppConfig.getInstance();
    LOGGER.info(appConfig.getApp().getCanalListenerIp());
    }

    }

说明:

  1. System.setProperty这里设置的系统启动参数,其实是没必要都在这里设置.我在这里设置时为了方便看.正真用的时候是设置在jvm启动参数中设置一个就行了.这个自定义参数的设置是在PropertiesConfig.getConfigPath方法中使用的.
  2. 见过其他人封装的PropertiesUtils工具类,建议这里类中读取配置的方法如果抛异常直接在这个类中处理就行了.没必要再向上抛了,因为属性的操作类就是这里出异常了应该在这里做统一处理.
  3. WebAppConfig中封装的内部类建议这么用,因为这样既可以统一管理配置信息又方便做细节区分.即使再多的配置也不会手忙脚乱,其实我要读取的配置更多,这里就列出来两个.
    以上.