单例形式-二十八线程情形

 

目录

  • 1.0立马加载/“饿汉式”
  • 二.0延缓加载/“懒汉式”
  • 三.0施用静态内置类完成单例方式
  • 4.0连串化与反类别化的单例方式完毕
  • 单例情势与二十四线程,第4章计算。伍.0用到static代码块达成单例形式
  • 6.0应用enum枚举数据类型实现单例形式
  • 柒.0健全利用enum枚举实现单例形式

 

正文的知识点特别关键,通过单例格局与八线程本事相结合,在那么些历程中能发现许多之前未思量过的气象,一些不良的先后设计艺术假使运用在购买贩卖项目中,将会遇上极其大的分神。本文的案例也将丰富表达,线程与一些本事相结合时要思考的作业有多数。在读书本文时只供给思考1件专门的学业,那正是:怎么样使单例形式蒙受八线程是平安的、准确的。

目录

  • 一.0及时加载/“饿汉式”
  • 二.0延迟加载/“懒汉式”
  • 3.0使用静态内置类完结单例方式
  • 四.0类别化与反种类化的单例格局完毕
  • 5.0运用static代码块达成单例格局
  • 陆.0采用enum枚举数据类型达成单例形式
  • 7.0周到利用enum枚举达成单例情势

 

  单例-霎时加载:

 1 /**
 2  *    单例模式,立即加载
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject = new MyObject();//立即加载(类加载的时候就已经实例化好了)
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         /**
12          *     此代码版本为立即加载
13          *     缺点是不能有其他实例变量,因为getInstance()方法没有同步,所有可能出现非线程安全问题
14          */
15         return myObject;
16     }
17 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类,创建三个线程分别去获取实例并输出hashCode值
 3  *        通过结果可以看出三个线程打印的hashCode值相同,说明对象是同一个,并且实现了立即加载型单例设计模式
 4  */
 5 public class Run {
 6 
 7     public static void main(String[] args) {
 8         MyThread t1 = new MyThread();
 9         MyThread t2 = new MyThread();
10         MyThread t3 = new MyThread();
11 
12         t1.start();
13         t2.start();
14         t3.start();
15     }
16 }

一.0即时加载/“饿汉式”

  登时加载:实用类的时候已经将指标创设完成,常见的兑现格局正是直接new实例化。

  注:是在调用方法前,就已经实例化了(常常是在类OPPO载的时候就曾经实例化了)。

public class Singleton {
    //立即加载方法==饿汉式
    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        //此代码版本为立即加载
        //此代码版本缺点:是不能有其他实例变量
        //因为getInstance()方法没有同步
        //所以可能出现非线程安全问题
        return singleton;
    }
}

在正儿捌经的二1个设计情势中,单例设计格局在行使中是比较宽泛的。但在健康的该格局教学资料介绍中,大多并未有结合10二线程技能作为仿效,那就招致在使用二10十六线程才能的单例格局时会出现部分想不到的情事,那样的代码假如在生育条件中出现格外,有希望引致患难性的后果。本文将介绍单例情势结合二10三十二线程手艺在选用时的有关知识。

一.0随即加载/“饿汉式”

  立时加载:实用类的时候曾经将指标创制完成,常见的达成方式就是从来new实例化。

  注:是在调用方法前,就已经实例化了(平常是在类华为载的时候就曾经实例化了)。

public class Singleton {
    //立即加载方法==饿汉式
    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        //此代码版本为立即加载
        //此代码版本缺点:是不能有其他实例变量
        //因为getInstance()方法没有同步
        //所以可能出现非线程安全问题
        return singleton;
    }
}

  单例-延迟加载:(该版本单例情势,倘诺在三三十二线程景况,则恐怕会冒出多个实例)

 1 /**
 2  *    单例设计类,延迟加载,当需要用再创建实例
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         if(myObject != null) {//延迟加载,也就是当用到了再创建
12         }else {
13             myObject = new MyObject();
14         }
15         return myObject;
16     }
17 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    如果是在多线程环境,会出现多个实例的情况,此时就与单例模式的初衷相违背了
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         t1.start();
12     }
13 }

二.0延缓加载/“懒汉式”

  延迟加载:在调用get()方法时实例才被创立,常见的完成格局正是在get()中开始展览实例化。

  注:在调用获取实例的形式时,实例才被创制。

public class Singleton {
    private static Singleton singleton;

    private Singleton(){}

    public static Singleton getInstance() {
        //延迟加载,若多个线程在此if失去执行权,最终会产生很多实例对象
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  能够运用synchronized方法恐怕synchronized同步代码块化解10二线程中的同步难点,可是功用异常低。

  书中提供了双检查锁机制:

  能够利用DCL双反省锁机制落到实处单例:

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton(){}
    //使用双检查锁机制来解决问题,既保证了不需要使用同步代码块的异步执行性,又保证了单例效果。

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

  注:关于双寻觅的冲突照旧有为数非常的多的,不过当前得以认为实际安全的。

  双检查锁的安全性难点总结(未到位)


 

1. 立即加载/“饿汉方式”

怎么样是即时加载?立时加载正是选用类的时候已经将对象创立完成,常见的贯彻情势正是一向new实例化。而及时加载从中文的语境来看,有“着急”、“火急”的意义,所以也称为“饿汉格局”。
当即加载/“饿汉形式”是在调用方法前,实例已经被创设了,来看一下贯彻代码。

开创测试用的品类,成立类MyObject.java代码如下:

public class MyObject {

    // 立即加载方式==饿汉模式
    private static MyObject myObject = new MyObject();

    private MyObject() {
    }

    public static MyObject getInstance() {
        // 此代码版本为立即加载
        // 此版本代码的缺点是不能有其它实例变量
        // 因为getInstance()方法没有同步
        // 所以有可能出现非线程安全问题
        return myObject;
    }

}

创制线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

创建运营类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运维后的结果如下:

1031548957
1031548957
1031548957

调整台打字与印刷的hashCode是同3个只,表明对象是同3个,也就落到实处了及时加载型单例设计方式。

2.0推迟加载/“懒汉式”

  延迟加载:在调用get()方法时实例才被创立,常见的贯彻方式就是在get()中张开实例化。

  注:在调用获取实例的办法时,实例才被创设。

public class Singleton {
    private static Singleton singleton;

    private Singleton(){}

    public static Singleton getInstance() {
        //延迟加载,若多个线程在此if失去执行权,最终会产生很多实例对象
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

  能够行使synchronized方法依然synchronized同步代码块消除二10八线程中的同步难点,可是效用十分低。

  书中提供了双反省锁机制:

  能够运用DCL双检查锁机制达成单例:

public class Singleton {
    private volatile static Singleton singleton;

    private Singleton(){}
    //使用双检查锁机制来解决问题,既保证了不需要使用同步代码块的异步执行性,又保证了单例效果。

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

  注:关于双找出的争持还是有这多少个的,不过最近能够感觉实际安全的。

  双检查锁的安全性难题总括(未产生)


 

  演示:延迟单例形式,出现多少个实例

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15                 myObject = new MyObject();
16             }
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20         return myObject;
21     }
22 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    通过运行结果可以看到打印的三个线程获取的对象的hashCode值不同,也就是出现了多例
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

三.0使用静态内置类完毕单例形式

public class Singleton {
    //内部类形式
    private static class MyHanlder {
        private static Singleton singleton = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return MyHanlder.singleton;
    }
}

2. 延缓加载/“懒汉形式”

什么样是延迟加载?延迟加载便是在调用get()方法时实例才被创制,常见的兑现格局正是在get()方法中开始展览new实例化。而延迟加载从中文的语境来看,是“缓慢”、“不热切”的意义,所以也叫做“懒汉形式”。

三.0行使静态内置类落成单例形式

public class Singleton {
    //内部类形式
    private static class MyHanlder {
        private static Singleton singleton = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return MyHanlder.singleton;
    }
}

  优化1:优化出现多例的状态,将全方位艺术加上贰头锁

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     //防止多线程出现多例,此处将整个方法加上同步锁(效率低)
11     synchronized public static MyObject getInstance() {
12         try {
13             if(myObject != null) {
14             }else {
15                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
16                 myObject = new MyObject();
17             }
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         }
21         return myObject;
22     }
23 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出是同步运行,但是整个方法加上同步锁,效率太低
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

肆.0体系化与反种类化的单例情势达成

  固然静态内置类能够直达到规定的分数线程安全,但1旦境遇系列化对象,使用私下认可的章程运转获得的依旧多例。

public class Singleton implements Serializable{
    private static final long seroalVersionUID = 888L;

    //内部类形式
    private static class MyHanlder {
        private static Singleton singleton = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return MyHanlder.singleton;
    }

    protected Object readResolve() throws ObjectStreamException {
        return MyHanlder.singleton;
    }
}

  书写readResolve()方法就足以幸免在种类化与反类别化的进度中单例失效。

  注:最近怎么那样还不明了。


 

二.一 延迟加载/“懒汉格局”解析

延期加载/“懒汉形式”是在调用方法时实例才被创设。一同来看一下跌成代码。

成立类MyObject.java代码如下:

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
    }

    public static MyObject getInstance() {
        // 延迟加载
        if (myObject != null) {
        } else {
            myObject = new MyObject();
        }
        return myObject;
    }

}

成立线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

始建运维类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }

}

程序运维后的坚守如下:

1031548957

此试验固然赢得二个对象的实例,但1旦是在多线程的条件中,就能够油不过生去除五个实例的图景,与单例形式的初衷是相违背的。

四.0种类化与反系列化的单例方式达成

  就算静态内置类可以到达线程安全,但壹旦蒙受连串化对象,使用暗中同意的艺术运营获得的依然多例。

public class Singleton implements Serializable{
    private static final long seroalVersionUID = 888L;

    //内部类形式
    private static class MyHanlder {
        private static Singleton singleton = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return MyHanlder.singleton;
    }

    protected Object readResolve() throws ObjectStreamException {
        return MyHanlder.singleton;
    }
}

  书写readResolve()方法就足防止止在连串化与反体系化的进度中单例失效。

  注:最近怎么那样还不晓得。


 

  再度优化:使用同步代码块

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     //防止多线程出现多例,在使用同步代码块
11     public static MyObject getInstance() {
12         try {
13             synchronized(MyObject.class) {
14                 if(myObject != null) {
15                 }else {
16                     Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
17                     myObject = new MyObject();
18                 }
19             }
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         return myObject;
24     }
25 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出是同步运行,修改成使用同步代码块,效率也是低因为整个方法的代码都在同步块中
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

5.0应用static代码块达成单例方式

  静态代码块中的代码在行使类的时候就早已施行了,所以能够动用静态代码块的那些特点来促成单例设计情势。

public class Singleton implements Serializable{
    private static Singleton singleton = null;

    private Singleton() {}

    static {
        singleton = new Singleton();
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

 


 

二.2 延迟加载/“懒汉格局”的瑕疵

眼下七个实验尽管选拔“马上加载”和“延迟加载”实现了单例设计情势,但在10贰线程的意况中,前边“延迟加载”示例中的代码完全便是错误的,根本补鞥呢实现保险单例的情景。来看一下如何在八线程景况中结成“错误的单例情势”成立出“多例”。
始建类MyObject.java代码如下:

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
    }

    public static MyObject getInstance() {
        try {
            if (myObject != null) {
            } else {
                // 模拟在创建对象之前做一些准备性的工作
                Thread.sleep(3000);
                myObject = new MyObject();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

}

创设线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

创办运维类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运营后的意义如下:

186668687
903341402
1770731361

调节台打印出了3中hashCode,表明创设了二个对象,并不是单例的,那就是“错误的单例格局”。怎么着缓和吧?先看一下化解方案。

五.0利用static代码块完结单例方式

  静态代码块中的代码在利用类的时候就已经进行了,所以能够采纳静态代码块的这些特点来兑现单例设计形式。

public class Singleton implements Serializable{
    private static Singleton singleton = null;

    private Singleton() {}

    static {
        singleton = new Singleton();
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

 


 

  继续优化:针对关键代码应用同步代码块

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15                 //虽然部分代码上锁,但是还是存在非线程安全问题
16                 synchronized(MyObject.class) {
17                     myObject = new MyObject();
18                 }
19             }
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         return myObject;
24     }
25 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出得到多个实例
 8      *        该优化只对实例对象的关键代码进行同步,结构上来说效率提升了,但是遇到多线程,还是无法解决得到同一个实例对象
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

陆.0施用enum枚举数据类型完成单例方式

  枚举与静态代码块类似,在行使枚举时,构造方法会被电动调用,也能够动用其那一个特点完毕单例设计形式。

 

 


 

二.三 延迟加载/“懒汉情势”的解决方案

六.0行使enum枚举数据类型完成单例格局

  枚举与静态代码块类似,在接纳枚举时,构造方法会被自动调用,也得以使用其那天性子完结单例设计情势。

 

 


 

  终极优化:使用DCL双检查锁机制落实二10多线程中延迟加载单例设计格局,解决出现多例的事态以及功用难题

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     //使用volatile关键字
 6     private volatile static MyObject myObject;
 7     
 8     private MyObject() {}
 9     
10     public static MyObject getInstance() {
11         try {
12             if(myObject != null) {
13             }else {
14                 Thread.sleep(3000);//模拟在创建对象之前做一些准备性工作
15                 synchronized(MyObject.class) {
16                     if(myObject == null) {
17                         myObject = new MyObject();
18                     }
19                 }
20             }
21         } catch (InterruptedException e) {
22             e.printStackTrace();
23         }
24         return myObject;
25     }
26 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出多个线程获得的是同一个对象
 8      *        使用双重检查锁功能,解决懒汉模式遇到的多线程问题
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

正文内容是书中剧情具备自身的村办见解所成。可能在私有理念上会有数不清难题(终究知识量有限,导致认识也许有数),假诺读者感到不符合规律请大胆提议,大家得以并行交流、互相学习,招待你们的过来,心成意足,等待你的褒贬。

 

 

2.3.1 声明synchronized关键字

既然如此两个线程能够而且进入getInstance()方法,那么只须求对getInstance()方法申明synchronized关键字就能够。

创办类MyObject.java代码如下:

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
    }

    // 设置同步方法效率太低了
    // 整个方法被上锁
    synchronized public static MyObject getInstance() {
        try {
            if (myObject != null) {
            } else {
                // 模拟在创建对象之前做一些准备性的工作
                Thread.sleep(3000);
                myObject = new MyObject();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

}

创造线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

创设运转类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运营后的结果如下:

531436928
531436928
531436928

此方法参预同步synchronized关键字获得平等实例的靶子,但此种方法的运作作用异常低下,是同步运维的,下一个线程想要获得对象,则必须等上3个线程释放锁之后,才干够继续实行。

本文内容是书中内容有着本身的村办观点所成。大概在民用观点上会有广大标题(究竟知识量有限,导致认识也轻巧),假诺读者感到失常请大胆提议,我们能够相互沟通、相互学习,欢迎你们的到来,心成意足,等待你的评价。

金沙注册送58 , 

 

  使用静态内部类达成单例:

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     //静态内部类
 7     public static class MyObjectHandler{
 8         private static MyObject myObject = new MyObject();
 9     }
10     
11     private MyObject() {}
12     
13     public static MyObject getInstance() {
14         return MyObjectHandler.myObject;
15     }
16 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         System.out.println(MyObject.getInstance().hashCode());
 9     }
10 }

 1 /**
 2  *    测试类
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出多个线程获得的是同一个对象
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

2.3.贰 尝试联机代码块

一起方法是对艺术的全体举行持锁,那对运作效用来说是不利的。改成3头代码块能缓和呢?

创建类MyObject.java代码如下:

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
    }

    public static MyObject getInstance() {
        try {
            // 此种写法等同于:
            // synchronized public static MyObject getInstance()
            // 的写法,效率一样很低,全部代码被上锁
            synchronized (MyObject.class) {
                if (myObject != null) {
                } else {
                    // 模拟在创建对象之前做一些准备性的工作
                    Thread.sleep(3000);

                    myObject = new MyObject();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

}

创制线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

创办运营类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

        // 此版本代码虽然是正确的
        // 但public static MyObject getInstance()方法
        // 中的全部代码都是同步的了,这样做有损效率
    }

}

程序运转后的结果如下:

903341402
903341402
903341402

此措施参与同步synchronized语句块获得1致实例的指标,但此种方法的运行效能也是非常的低的,和synchronized同步方法1致是手拉手运转的。继续改换代码尝试消除这些毛病。

  连串化与反连串化的单例设计情势:

 1 import java.io.Serializable;
 2 
 3 /**
 4  *    延迟加载单例模式类
 5  */
 6 public class MyObject implements Serializable{
 7     
 8     private static final long serialVersionUID = 1L;
 9 
10     //静态内部类
11     public static class MyObjectHandler{
12         private static MyObject myObject = new MyObject();
13     }
14     
15     private MyObject() {}
16     
17     public static MyObject getInstance() {
18         return MyObjectHandler.myObject;
19     }
20     //保证反序列化拿到的对象与序列化对象是同一个对象
21     protected Object readResolve() {
22         System.out.println("调用了readResolve()方法");
23         return MyObjectHandler.myObject;
24     }
25 }

 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutputStream;
 8 
 9 /**
10  *    测试类
11  */
12 public class Run {
13     public static void main(String[] args) {
14         //将对象序列化到文件
15         try {
16             MyObject myObject = MyObject.getInstance();
17             FileOutputStream fops = new FileOutputStream(new File("myObjectFile.text"));
18             ObjectOutputStream oops = new ObjectOutputStream(fops);
19             oops.writeObject(myObject);
20             oops.close();
21             fops.close();
22             System.out.println(myObject.hashCode());
23         } catch (FileNotFoundException e) {
24             e.printStackTrace();
25         } catch (IOException e) {
26             e.printStackTrace();
27         }
28         //将文件中序列化的对象反序列化并输出到控制台
29         try {
30             FileInputStream fips = new FileInputStream(new File("myObjectFile.text"));
31             ObjectInputStream oips = new ObjectInputStream(fips);
32             MyObject readObject = (MyObject)oips.readObject();
33             oips.close();
34             fips.close();
35             System.out.println(readObject.hashCode());
36         } catch (FileNotFoundException e) {
37             e.printStackTrace();
38         } catch (IOException e) {
39             e.printStackTrace();
40         } catch (ClassNotFoundException e) {
41             e.printStackTrace();
42         }
43     }
44 }

二.3.三 针对少数关键的代码实行单独的同台

联机代码块能够本着一些关键的代码进行单独的一齐,而其他的代码则不需求一同。这样在运营时,成效完全可以获得大幅度进步。

创制MyObject.java代码如下:

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
    }

    public static MyObject getInstance() {
        try {
            if (myObject != null) {
            } else {
                // 模拟在创建对象之前做一些准备性的工作
                Thread.sleep(3000);
                // 使用synchronized (MyObject.class)
                // 虽然部分代码被上锁
                // 但还是有非线程安全问题
                synchronized (MyObject.class) {
                    myObject = new MyObject();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }

}

创制线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

创建运营类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运维后的结果如下:

903341402
1770731361
1684444739

此情势使同步synchronized语句块,只对实例化对象的重中之重代码举办共同,从言语的构造上来说,运转的功用确实获得了进级。但纵然是碰着102线程的气象下十分的小概解决获得同1个实例对象的结果。到底哪些减轻“懒汉方式”蒙受三十二线程的图景吗?

  使用静态代码块完毕单例:

 1 /**
 2  *    延迟加载单例模式类
 3  */
 4 public class MyObject {
 5     
 6     private static MyObject instance = null;
 7     
 8     private MyObject() {}
 9     
10     static {
11         instance = new MyObject();
12     }
13     
14     public static MyObject getInstance() {
15         return instance;
16     }
17 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.getInstance().hashCode());
10         }
11     }
12 }

 1 /**
 2  *    测试类,测试使用静态代码块实现单例
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出多个线程获得的是同一个对象
 8      */
 9     public static void main(String[] args) {
10         MyThread t1 = new MyThread();
11         MyThread t2 = new MyThread();
12         MyThread t3 = new MyThread();
13         t1.start();
14         t2.start();
15         t3.start();
16     }
17 }

二.3.4 使用DCL双反省锁机制

在最后的步子中,使用的是DCL双检查锁机制来促成二1010二线程情形中的延迟加载单例设计情势。

创制类MyObject.java代码如下:

public class MyObject {

    private volatile static MyObject myObject;

    private MyObject() {
    }

    // 使用双检测机制来解决问题
    // 即保证了不需要同步代码的异步
    // 又保证了单例的效果
    public static MyObject getInstance() {
        try {
            if (myObject != null) {
            } else {
                // 模拟在创建对象之前做一些准备性的工作
                Thread.sleep(3000);
                synchronized (MyObject.class) {
                    if (myObject == null) {
                        myObject = new MyObject();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return myObject;
    }
    // 此版本的代码称为:
    // 双重检查Double-Check Locking

}

成立线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

开创运转类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {


        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运营后的结果如下:

186668687
186668687
186668687

动用重复检查锁效率,成功地消除了“懒汉形式”遇到十贰线程的难点。DCL也是半数以上拾2线程结合单例方式选拔的化解方案。

  使用enum枚举数据类型完结单例方式:

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.SQLException;
 4 
 5 /**
 6  *    使用enum枚举类型实现单例模式,此处的枚举类进行了暴露,违反了职责单一原则
 7  */
 8 public enum MyObject {
 9     
10     connectionFactory;
11     
12     private Connection connection;
13     
14     private MyObject() {
15         try {
16             System.out.println("调用MyObject的构造方法");
17             String url = "";
18             String user = "";
19             String password = "";
20             String driverName = "";
21             Class.forName(driverName);
22             connection = DriverManager.getConnection(url, user, password);
23         } catch (ClassNotFoundException e) {
24             e.printStackTrace();
25         } catch (SQLException e) {
26             e.printStackTrace();
27         }
28     }
29     
30     public Connection getConnection() {
31         return connection;
32     }
33 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.connectionFactory.getConnection().hashCode());
10         }
11     }
12 }

 1 /**
 2  *    测试类,测试使用枚举数据类型实现单例
 3  */
 4 public class Run {
 5 
 6     /**
 7      *    测试结果可以看出多个线程获得的是同一个对象
 8      *        枚举与静态代码块相似,在使用枚举类时,构造方法自动被调用
 9      */
10     public static void main(String[] args) {
11         MyThread t1 = new MyThread();
12         MyThread t2 = new MyThread();
13         MyThread t3 = new MyThread();
14         t1.start();
15         t2.start();
16         t3.start();
17     }
18 }

3. 用到静态内置类达成单例形式

DCL能够缓和多线程单例方式的非线程安全主题素材。当然,使用其余的不二法门也能达到规定的标准同等的效能。

成立类MyObject.java代码如下:

public class MyObject {

    // 内部类方式
    private static class MyObjectHandler {
        private static MyObject myObject = new MyObject();
    }

    private MyObject() {
    }

    public static MyObject getInstance() {
        return MyObjectHandler.myObject;
    }

}

成立线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }

}

成立运维类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运营后的结果如下:

531436928
531436928
531436928

  完善利用enum枚举数据类型达成单例情势:

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.SQLException;
 4 
 5 /**
 6  *    使用enum枚举类型实现单例模式
 7  */
 8 public class MyObject {
 9     public enum MyEnumSingletion{
10         connectionFactory;
11         
12         private Connection connection;
13         
14         private MyEnumSingletion() {
15             try {
16                 System.out.println("调用MyObject的构造方法");
17                 String url = "";
18                 String user = "";
19                 String password = "";
20                 String driverName = "";
21                 Class.forName(driverName);
22                 connection = DriverManager.getConnection(url, user, password);
23             } catch (ClassNotFoundException e) {
24                 e.printStackTrace();
25             } catch (SQLException e) {
26                 e.printStackTrace();
27             }
28         }
29         
30         public Connection getConnection() {
31             return connection;
32         }
33     }
34     
35     public static Connection getConnection() {
36         return MyEnumSingletion.connectionFactory.getConnection();
37     }
38 }

 1 /**
 2  *    线程类
 3  */
 4 public class MyThread extends Thread {
 5     
 6     @Override
 7     public void run() {
 8         for (int i = 0; i < 5; i++) {
 9             System.out.println(MyObject.getConnection().hashCode());
10         }
11     }
12 }

 1 /**
 2  *    测试类,测试使用枚举数据类型实现单例
 3  */
 4 public class Run {
 5 
 6     public static void main(String[] args) {
 7         MyThread t1 = new MyThread();
 8         MyThread t2 = new MyThread();
 9         MyThread t3 = new MyThread();
10         t1.start();
11         t2.start();
12         t3.start();
13     }
14 }

4. 连串化与反体系化的单例情势实现

静态内置类能够直达到规定的分数线程安全难题,但要是遇到类别化对象时,使用暗中认可的章程运转获得的结果依然多例的。

创制MyObject.java代码如下:

import java.io.ObjectStreamException;
import java.io.Serializable;

public class MyObject implements Serializable {

    private static final long serialVersionUID = 888L;

    // 内部类方式
    private static class MyObjectHandler {
        private static final MyObject myObject = new MyObject();
    }

    private MyObject() {
    }

    public static MyObject getInstance() {
        return MyObjectHandler.myObject;
    }

    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调用了readResolve方法!");
        return MyObjectHandler.myObject;
    }

}

始建工作类SaveAndRead.java代码如下:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import test.MyObject;

public class SaveAndRead {

    public static void main(String[] args) {
        try {
            MyObject myObject = MyObject.getInstance();
            FileOutputStream fosRef = new FileOutputStream(new File(
                    "myObjectFile.txt"));
            ObjectOutputStream oosRef = new ObjectOutputStream(fosRef);
            oosRef.writeObject(myObject);
            oosRef.close();
            fosRef.close();
            System.out.println(myObject.hashCode());
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try {
            FileInputStream fisRef = new FileInputStream(new File(
                    "myObjectFile.txt"));
            ObjectInputStream iosRef = new ObjectInputStream(fisRef);
            MyObject myObject = (MyObject) iosRef.readObject();
            iosRef.close();
            fisRef.close();
            System.out.println(myObject.hashCode());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}

程序运转后的效益如下:

1836019240
81628611

消除办法正是在反类别化中动用readResolve()方法。
去掉如下代码的讲解:

    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调用了readResolve方法!");
        return MyObjectHandler.myObject;
    }

程序运维后的结果如下:

1836019240
调用了readResolve方法!
1836019240

5. 应用static代码块达成单例方式

静态代码块中的代码在使用类的时候就早已实施了,所以能够应用静态代码块的那些特点来贯彻单例设计形式。

创建MyObject.java代码如下:

public class MyObject {

    private static MyObject instance = null;

    private MyObject() {
    }

    static {
        instance = new MyObject();
    }

    public static MyObject getInstance() {
        return instance;
    }

}

成立线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(MyObject.getInstance().hashCode());
        }
    }
}

开创运维类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }

}

程序运维后的结果如下:

186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687
186668687

六. 用到enum枚举数据类型达成单例格局

枚举enum和静态代码块的天性相似,在使用枚举类时,构造方法会被电动调用,也得以应用其那么些特点完结单例设计方式。

始建类MyObject.java代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public enum MyObject {
    connectionFactory;

    private Connection connection;

    private MyObject() {
        try {
            System.out.println("调用了MyObject的构造");
            String url = "jdbc:mysql://localhost:3306/test";
            String username = "root";
            String password = "root";
            String driverName = "com.mysql.jdbc.Driver";
            Class.forName(driverName);
            connection = DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection() {
        return connection;
    }
}

创设线程类MyThread.java代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(MyObject.connectionFactory.getConnection()
                    .hashCode());
        }
    }
}

创办运转类Run.java代码如下:

import extthread.MyThread;

public class Run {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.start();
        t2.start();
        t3.start();

    }
}

程序运行后的结果如下:

调用了MyObject的构造
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510
482793510

七. 完美应用enum枚举完毕单例情势

近期一节将枚举类实行暴光,违反了“任务单1原则”,在本节中开始展览完善。

退换类MyObject.java代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MyObject {

    public enum MyEnumSingleton {
        connectionFactory;

        private Connection connection;

        private MyEnumSingleton() {
            try {
                System.out.println("创建MyObject对象");
                String url = "jdbc:mysql://localhost:3306/test";
                String username = "root";
                String password = "root";
                String driverName = "com.mysql.jdbc.Driver";
                Class.forName(driverName);
                connection = DriverManager.getConnection(url, username,
                        password);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        public Connection getConnection() {
            return connection;
        }
    }

    public static Connection getConnection() {
        return MyEnumSingleton.connectionFactory.getConnection();
    }

}

变动MyThread.java类代码如下:

import test.MyObject;

public class MyThread extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(MyObject.getConnection().hashCode());
        }
    }
}

程序运转的结果如下:

创建MyObject对象
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269
2007882269

相关文章

网站地图xml地图