单例设计模式
8种单例模式
-
单例模式:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
-
单例模式有八种方式:
1) 饿汉式(静态常量)
2) 饿汉式(静态代码块)
3) 懒汉式(线程不安全)
4) 懒汉式(线程安全,同步方法)
5) 懒汉式(线程安全,同步代码块)
6) 双重检查
7) 静态内部类
8) 枚举
之前学java基础的时候页写过单例的笔记,只包括饿汉式和懒汉式 -
步骤都一样的(除枚举类):
- 构造器私有化 (防止 new )
- 类的内部创建对象
- 向外暴露一个静态的公共方法。 getInstance
用来测试的代码都一样(除枚举类)
public class SingletonTest1 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1==singleton2);
}
}
饿汉式(可用,但可能造成内存浪费)
-
饿汉式就是类加载对象就加载,这样如果我们没有用到这个类的时候 就造成了资源浪费.
但是饿汉式是线程安全的,所以这种单例模式是可用的. -
饿汉式优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同
步问题。 -
饿汉式缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
这里的类装载就完成实例化,如果是通过getInstance方式的时候导致的类装载,那这不是资源浪费.但是如果不是通过这种方法,而是别的导致类装载的,那就是没有达到懒加载的效果
代码:
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static Singleton singleton= new Singleton();
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
return singleton;
}
}
静态代码块
静态代码块的方式就是把 new Singleton()的部分放在了静态代码块里,其余部分不做改动
静态代码块也是在类加载的时候加载的
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static Singleton singleton;
static{
singleton = new Singleton();
}
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
return singleton;
}
}
懒汉式
懒汉式是不用就不造,到用的时候才造
线程不安全 的 懒汉式 (不能使用)
这样如果多线程的情况下,线程A进入 if(singletonnull)里,但还没创建出来对象,同时线程B也进入 if(singletonnull)里,这样就会创建出来两个对象,就不是单例设计模式了.
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static Singleton singleton;
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
//当调用getInstance 才创建对象
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
线程安全的懒汉式 同步方法(不推荐使用,效率低)
把getSingleton() 加synchronized关键字变成同步方法,这样就既是懒加载又是线程同步了.
但是我们实例只创建一次,大部分情况都只是return就好,这样每次获取实例的时候都要等,效率太低.
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static Singleton singleton;
//向类的外部暴露一个静态的公共方法
public static synchronized Singleton getSingleton() {
//当调用getInstance 才创建对象
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
懒汉式 同步代码块(不能使用,未实现线程安全)
把if判断的部分用同步代码块包住,想着这样就解决了同步方法效率不高的问题.
但这样并不能实现线程安全.这样还是会假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
另一个线程也通过了这个判断语句,这时便会产生多个实例
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static Singleton singleton;
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
//同步代码块
if(singleton==null){
synchronized(Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
双重检查(推荐使用)
- 我们可以在同步代码块里再加一个判断 if(singleton==null),这样就解决问题,了
- 注意要加volatile
volatile 可以让我们的修改值立即更新到主存,可以理解成轻量级的synchronized. 这样如果有共享的变量,一旦有修改值立马就刷到主存里. - 优点:线程安全;延迟加载;效率较高
class Singleton{
// 构造器私有化
private Singleton(){
}
//类的内部创建对象
private static volatile Singleton singleton;
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
//同步代码块
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类(推荐使用)
静态内部类的特点:
- 类Singleton被装载的时候,静态内部类SingletonInstance是不会被装载的
- getInstance方法调静态内部类的静态属性的时候.会导致静态内部类的装载,且只会装载一次,且装载的时候是线程安全的
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
class Singleton{
// 构造器私有化
private Singleton(){
}
//静态内部类
private static class SingletonInstance{
private static final Singleton SINGLETON = new Singleton();
}
//向类的外部暴露一个静态的公共方法
public static Singleton getSingleton() {
return SingletonInstance.SINGLETON;
}
}
枚举(推荐使用)
- 优点:1) 这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
- 这种方式是Effective Java作者Josh Bloch 提倡的方式
enum Singleton{
SINGLETON;
public void method(){
}
}
测试:
public class SingletonTest1 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.SINGLETON;
Singleton singleton2 = Singleton.SINGLETON;
System.out.println(singleton1==singleton2);
}
}
单例模式JDK源码分析
我们JDK中, java.lang.Runtime就是经典的单例模式(饿汉式)
单例模式注意事项和细节说明
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
单例模式的使用场景
单例模式使用的场景:
- 需要频繁的进行创建和销毁的对象
- 创建对象时耗时过多或耗费资源过多(即:重量级对象), 但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、 session工厂等)