前言
设计模式中的单例模式,大家基本也都会,也都基本上都知道两种实现形式,无外乎饿汉式和饱汉式两种。其实在这两种方式中可以衍生出8中方式。
方式一: 饿汉式
public class Student {
private static final Student INSTANCE = new Student();
private Student () {
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
return INSTANCE;
}
}
饿汉式,类一加载就实例化对象,私有化构造方法,提供开放的获取实例的方法。该方式也是最为简单也推荐的单例使用方式。
方式二:(和第一种相同)
public class Student {
private static final Student INSTANCE = null;
static {
INSTANCE = new Student();
}
private Student () {
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
return INSTANCE;
}
}
大家会就觉得这两种方式的实现,不管类用不用类一加载就实例化了,占用内存空间,不是太完美,然后就有了下面的饱汉汉式的懒加载方式的单例。
方式三 : 饱汉式
public class Student{
private static Student INSTANCE = null;
private Student() {
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
if (INSTANCE == null) {
INSTANCE = new Student();
}
return INSTANCE;
}
}
该中方式,实现了在调用的时候,在实例化对象,实现了懒加载,看似很完美了,其实这样会出现线程并发访问时的线程不安全问题。当两个线程同时访问getInstance()方法时,同时看到的INSTANCE都是null,所以两个线程会各自会创建一个新的实例对象。从而引发了我们的第四种实现方式。
方式四:(synchronized同步)
public class Student{
private static volatile Student INSTANCE = null;
private Student() {
}
public void method(){
System.out.println("method");
}
public static synchronized Student getInstance() {
if (INSTANCE == null) {
INSTANCE = new Student();
}
return INSTANCE;
}
}
这种方式采用同步方法的后,实现了线程安全,但是却降低了程序的性能。因为每次调用都要加锁。
那么这用方式有完美的方式吗?那么接下来,我们继续来看方式五。
方式五:(synchronized代码块)
public class Student{
private static volatile Student INSTANCE = null;
private Student() {
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
if (INSTANCE == null) {
synchronized(Student.class){
INSTANCE = new Student();
}
}
return INSTANCE;
}
}
这种方式,是不是突然感觉优雅了不少,当我创建实例的时候,我给它加锁,这样不就不是给整个方法加锁了,可以提升一点性能。但是,一说但是,当时还是不完美的,大家仔细想一下,当进入方法体,判断INSTANCE == null 时,还是会出现问题的,此时两个线程并发访问,都通过了if(INSTANCE == null){
这一样代码,还是会各自创建一个实例对象。导致线程不安全。是不是有一种压迫感,问题一个接着一个。没关系啦,接下来,我们就来实现终极的优雅版本。
方法六:(synchronized+ 双重检查)
public class Student{
private static volatile Student INSTANCE = null;
private Student() {
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
if (INSTANCE == null) {
synchronized (Student.class) {
if (INSTANCE == null){
INSTANCE = new Student();
}
}
}
return INSTANCE;
}
}
此种方式,也就是线程安全的饱汉式方式了,也是可以推荐使用的
。采用双重检查和同步代码块实现,在第一次判断了INSTANCE
是否为null
之后,进入同步,此时再次进行判断,如果实例为null
,则进行创建新的实例,若不是null
了,不做任何操作,直接返回实例。
方式七:(静态内部类的方式)
public class Student{
private Student() {
}
private static class StudentHolder {
private final static Student INSTANCE = new Student();
}
public void method(){
System.out.println("method");
}
public static Student getInstance() {
return StudentHolder.INSTANCE;
}
}
此种方式,使用静态内部类来创建实例对象,从而实现懒加载。当Student类加载的时候,是不会加载StudentHolder类的。只有调用的getInstance方法后才会加载,从而实现懒加载。通过JVM来保证单例。此种方式,也强烈推荐使用。
方式八:(枚举类实现)
public enum Mgr08 {
INSTANCE;
public void m() {
System.out.println("哈哈哈,我成功了");
}
}
此种方式不仅可以解决线程同步,还可以防止反序列化。也是即为简便的一种方式,但是我个人感觉怪怪的,毕竟好好的一类,整成了一个枚举。也是推荐大家使用。
总结
以上,便是八种单例的实现方式,小编比较推荐使用方式一、方式二、方式六、方式七、方式八,最为推荐使用方式一,简单还安全。