Java多线程相关知识【8】--设计模式--Singleton方式

[TOC]

# Java多线程相关知识【8】--设计模式--Singleton方式

## 一、四种单例模式(Singleton)的优缺点在多线程下

### 1.饿汉式

优点:实现简单。

缺点:使用前即主动加载,若很长时间未使用,易造成内存浪费。

代码实现如下所示:

```java
/**
* 饿汉式单例模式
*/
public class SingelazeHangers {
//创建类时即创建了元素
private static SingelazeHangers instance = new SingelazeHangers();

private SingelazeHangers() {
}

/**
* 真正执行时调用此元素
* @return 返回单例设计
*/
public static SingelazeHangers getInstance() {
return instance;
}

//测试实例加载
public static void main(String[] args) {
SingelazeHangers s=SingelazeHangers.getInstance();
}
}
```



### 2.懒汉式

优点:需要时才会加载,减少内存的浪费。

缺点:多个线程同时获取时,可能会被创建多次,易产生问题,可能不是单个实例。

```java
/**
* 懒汉式单例模式
*/
public class SingelazeLazyers {
//创建时并未创建类
public static SingelazeLazyers instance = null;

private SingelazeLazyers() {
}

/**
* 需要时进行类的创建
* @return 返回创建的结果
*/
public static SingelazeLazyers getInstance() {
if (instance == null)
instance = new SingelazeLazyers();
return instance;
}

//测试单例模式
public static void main(String[] args) {
SingelazeLazyers s=SingelazeLazyers.getInstance();
}
}
```

### 3.懒汉式改进

#### 1.函数加锁

优点:解决懒汉加载可能创建多个实例的问题。

缺点:性能低下。

说明:多线程使用时,虽然会解决创建非单例的问题,但是,在创建后,所有的线程依然需要进行等待,会浪费大量的时间进行单例的获取。

代码实现如下:

```java
/**
* 懒汉式单例模式(加类锁)
*/
public class SingelazeLazyersClassLock {
//创建时并未创建类
public static SingelazeLazyersClassLock instance = null;

private SingelazeLazyersClassLock() {
}

/**
* 需要时进行类的创建(关键字在此添加)
* @return 返回创建的结果
*/
public synchronized static SingelazeLazyersClassLock getInstance() {
if (instance == null)
instance = new SingelazeLazyersClassLock();
return instance;
}

//测试单例模式
public static void main(String[] args) {
SingelazeLazyersClassLock s= SingelazeLazyersClassLock.getInstance();
}
}
```

#### 2.双重判断

优点:解决函数加锁产生的性能问题。

缺点:易产生空指针异常。

说明:多线程使用时,第一个获取的元素释放了相关的锁,但是,对象内部的内容并未创建完成,导致其他线程调用到方法时,虽然拿到了对象,但对象内,需要使用的方法并未被实例化。

代码实现如下:

```java
/**
* 懒汉式单例模式(双重锁)
*/
public class SingelazeLazyersDoubleCheck {
//创建时并未创建类
public static SingelazeLazyersDoubleCheck instance = null;

private SingelazeLazyersDoubleCheck() {
}

/**
* 需要时进行类的创建(内容在此修改)
*
* @return 返回创建的结果
*/
public static SingelazeLazyersDoubleCheck getInstance() {
if (instance == null) {
synchronized (SingelazeLazyersDoubleCheck.class) {
if (instance == null)
instance = new SingelazeLazyersDoubleCheck();
}
}
return instance;
}

//测试单例模式
public static void main(String[] args) {
SingelazeLazyersDoubleCheck s = SingelazeLazyersDoubleCheck.getInstance();
}
}
```

## 二、三种优雅实现单例的方法

### 1.双重判断的改进

​ volatile关键字非原子性的,他可保证内存是可见的,并能够指定编译器及虚拟机不可对相关的语句进行优化,并保证在读相应内存区时,相关元素的创建必须完成。

​ 缺点:虚拟机未优化,执行效率低下。

```java
/**
* 懒汉式单例模式(双重锁,内存可见式)
*/
public class SingelazeLazyersDoubleCheckVolatile {
//创建时并未创建类(关键字在此添加)
public volatile static SingelazeLazyersDoubleCheckVolatile instance = null;

private SingelazeLazyersDoubleCheckVolatile() {
}

/**
* 需要时进行类的创建
*
* @return 返回创建的结果
*/
public static SingelazeLazyersDoubleCheckVolatile getInstance() {
if (instance == null) {
synchronized (SingelazeLazyersDoubleCheckVolatile.class) {
if (instance == null)
instance = new SingelazeLazyersDoubleCheckVolatile();
}
}
return instance;
}

//测试单例模式
public static void main(String[] args) {
SingelazeLazyersDoubleCheckVolatile s = SingelazeLazyersDoubleCheckVolatile.getInstance();
}
}
```

### 2.等待加载方式(不加锁)

​ 利用java的类加载机制,保证了单例模式的准确性,由于static关键字,保证了初始化的单一性。

代码实现如下:

```java
/**
* 等待加载模式
*/
public class SingelazeHoder {
private SingelazeHoder() {

}

/**
* 内部私有类
*/
private static class InstanceHoderHelper{
private static final SingelazeHoder instance=new SingelazeHoder();
}

/**
* 获取单例元素
* @return 单例元素
*/
public static SingelazeHoder getInstance(){
return InstanceHoderHelper.instance;
}

/**
* 其他类引用时的使用方法
* @param args
*/
public static void main(String[] args) {
SingelazeHoder s=SingelazeHoder.getInstance();
}
}
```

### 3.利用枚举的方式实现

枚举类型是私有的,且为线程安全的,且在构造时,只会被装载一次,故能实现相关的单例模式。

```java
/**
* 利用枚举实现单例模式
*/
public class SingletonEnum {
private SingletonEnum(){}
//利用枚举
private enum Singleton {
INSTANCE;
private SingletonEnum instance=null;
Singleton(){
instance=new SingletonEnum();
}

public SingletonEnum getInstance() {
return instance;
}
}
//获取单例信息
public static SingletonEnum getInstance(){
return Singleton.INSTANCE.getInstance();
}

//其他类使用时调用方法
public static void main(String[] args) {
SingletonEnum s=SingletonEnum.getInstance();
}
}

```

0 个评论

要回复文章请先登录注册