单例模式
所谓累的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
1.饿汉式(静态常量)
这种方式的缺点就是可能会造成内存的浪费,因为不管我们是否会用到这个对象,这个对象都会在内存重创建,如果可以确保,这个对象在程序运行期间一定可以用到,那么使用饿汉式是不错的选择
package com.designpatten.singleton.type1;
public class Singleton01 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
//饿汉式(静态变量)
class Singleton{
//1.构造器私有化
private Singleton(){
}
//2.在奔雷内部创建对象实例
private final static Singleton instance = new Singleton();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
2.饿汉式(静态代码块)
package com.designpatten.singleton.type2;
/**
* 同步代码块的写法
*/
public class Singleton02 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
class Singleton{
private Singleton(){
}
private final static Singleton instance;
static {
instance = new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
3.懒汉式(线程不安全)
懒汉式可以实现懒加载,但是现在这种方式是线程不安全的
package com.designpatten.singleton.type3;
public class Singleton03 {
public static void main(String[] args) {
Singleton s1 = new Singleton();
Singleton s2 = new Singleton();
System.out.println(s1 == s2);
}
}
class Singleton{
private Singleton(){}
private static Singleton instance;
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
下面的程序加入了多线程,演示了这种方式线程不安全的情况
package com.designpatten.singleton.type3;
public class Singleton03 {
public static void main(String[] args) {
int num = 2;
Singleton[] s= new Singleton[num];
for (int i = 0; i < num; i++) {
final int index = i;
new Thread(() -> {
s[index] = Singleton.getInstance();
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s[0]);
System.out.println(s[1]);
System.out.println(s[0] == s[1]);
}
}
class Singleton{
private Singleton(){}
private static Singleton instance;
public static int i = 0;
public static Singleton getInstance(){
if (instance == null){
System.out.println("我进来了");
i++;
if (i == 0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else if (i == 1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
instance = new Singleton();
}
return instance;
}
}
4.懒汉式(线程安全,同步方法)
这种写法就是在上面的懒汉式的基础上加了一个synchronized解决了线程不安全的问题,但是却影响了效率,如果有很多个线程同时访问这个方法就会阻塞
这里对于synchronized不太了解的可以看这篇
package com.designpatten.singleton.type3;
public class Singleton03 {
public static void main(String[] args) {
Singleton s1 = new Singleton();
Singleton s2 = new Singleton();
System.out.println(s1 == s2);
}
}
class Singleton{
private Singleton(){}
private static Singleton instance;
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
5.双重检查
这种方式没有线程安全的问题,对于性能的损耗也不是很大,一般推荐使用这种方式
package com.designpatten.singleton.type4;
import java.util.concurrent.TimeUnit;
/**
* 双重检查:推荐使用这个比较多
*/
public class Singleton4 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton.method1();
}
}
class Singleton{
private static volatile Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
6.静态内部类
静态内部类是不会自动去加载的,只有等需要用到的时候才会加载,所以这种方式是懒加载,而且在类加载的时候线程是天然安全的,所以这种方式也没有线程安全的问题,推荐使用
package com.designpatten.singleton.type5;
public class Singleton05 {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
/**
* 使用静态内部类的方式去创建单例对象
*/
class Singleton{
private static volatile Singleton instance;
private Singleton() {
}
static class SingletonInstance{
private static Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
7.枚举
使用这种方式来实现的单例模式,不仅能避免多线程的同步问题,也可以防止反序列化重新创建新的对象,比较推荐使用这种方式
package com.designpatten.singleton.type6;
public class Singleton06 {
public static void main(String[] args) {
Singleton s1 = Singleton.INSTANCE;
Singleton s2 = Singleton.INSTANCE;
System.out.println(s1 == s2);
s1.method01();
}
}
enum Singleton{
INSTANCE;
public void method01(){
System.out.println("我是方法1");
}
}