1.饿汉式(静态常量)
package com.singleton;
public class Test1 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1==instance2);
}
static class Singleton{
private Singleton(){}
private final static Singleton SINGLETON = new Singleton();
public static Singleton getInstance(){
return SINGLETON;
}
}
}
通过private修饰构造方法使其不能实例化,只能通过get方法调用,通过打印结果可以看出两次调用使用的都是同一个对象
优点:写法简单,再类装载的时候就完成了实例化,避免了线程同步的问题
缺点:没有达到lazy loading(延迟加载,懒加载)的效果,如果从始至终没有使用到这个实例就会造成内存浪费
2.饿汉式(静态代码块)
package com.singleton;
public class Test2 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private static Singleton SINGLETON;
static {
new Singleton();
}
public static Singleton getInstance() {
return SINGLETON;
}
}
}
静态代码块的写法和上面静态常量的写法差不多,优缺点也是一样的
3.懒汉式(线程不安全)
package com.singleton;
public class Test3 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private static Singleton SINGLETON;
public static Singleton getInstance() {
if (SINGLETON==null){
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
}
懒汉式不会在类装载的时候创建实例,而实在调用get方法时,判断是否已创建,如果没有才创建,但是这种方式在多线程环境下会存在风险,加入在创建实例前有多个线程都通过了if null 的判断,这样就会创建多个实例,所以多线程环境这种方法是不推荐使用的
4.懒汉式(线程安全,同步方法)
package com.singleton;
public class Test4 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private static Singleton SINGLETON;
public synchronized static Singleton getInstance() {
if (SINGLETON==null){
SINGLETON = new Singleton();
}
return SINGLETON;
}
}
}
同步方法的方式可用,但是效率低
5.懒汉式(线程安全,同步代码块)
package com.singleton;
public class Test5 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private static Singleton SINGLETON;
public static Singleton getInstance() {
synchronized (Test5.class) {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
}
return SINGLETON;
}
}
}
同步代码块与同步方法一样,也是可用的,但是效率比较低
6.双重检查
package com.singleton;
public class Test6 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private volatile static Singleton SINGLETON;
public static Singleton getInstance() {
if (SINGLETON == null) {
synchronized (Test6.class) {
if (SINGLETON == null) {
SINGLETON = new Singleton();
}
}
}
return SINGLETON;
}
}
}
在同步代码块前再套一层if这样就不会每一次线程进入方法都被synchronized卡住,就能优化效率问题了,再给SINGLETON加上volatile关键字也能知道其他线程对SINGLETON的操作
7.静态内部类
package com.singleton;
public class Test7 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2);
}
static class Singleton {
private Singleton() {
}
private static class SingletonInstance{
private final static Singleton SINGLETON = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.SINGLETON;
}
}
}
静态内部类实现单例的好处就是类加载时候是不会加载静态内部类的,只有第一次调用的时候才会加载,并且jvm转载类的时候是线程安全的,所以这种方式既可以实现懒加载也可以保证线程安全,是推荐使用的
8.枚举
package com.singleton;
public class Test8 {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance;
Singleton instance2 = Singleton.getInstance;
System.out.println(instance1 == instance2);
}
enum Singleton {
getInstance();
Singleton() {
}
}
}
用枚举的方式也是可以实现单例的效果的