深入理解java多线程(六)

关于java多线程的概念以及基本用法:java多线程基础

6,单例模式与多线程

如何使单例模式遇到多线程是安全的这是下面要讨论的内容

6.1,立即加载

立即加载就是在使用类的时候已经将对象创建完毕,例如String s = new String(”hello world!”);这里就是直接将对象实例化了
MyObject类:

public class MyObject {

    private static MyObject myObject = new MyObject();

    private MyObject() {

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

测试类:

public class Run extends Thread{

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

    public static void main(String[] args) {
        Run run1 = new Run();
        Run run2 = new Run();
        Run run3 = new Run();

        run1.start();
        run2.start();
        run3.start();
    }
}

结果:

1141021789
1141021789
1141021789

打印的hashCode是同一个值,说明对象时同一个

6.2,延迟加载

延迟加载就是在调用get()方法时实例才被创建,常见的是在
get()方法中进行new实例化

MyObject类:

public class MyObject{

    public static MyObject myObject;
    private MyObject() {

    }
    public static MyObject getInstance()  {
        if(myObject != null) {
        }else {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myObject = new MyObject();
        }
        return myObject;
    }
}

测试类:

public class Run extends Thread{

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

    public static void main(String[] args) {
        Run run1 = new Run();
        Run run2 = new Run();
        Run run3 = new Run();
        run1.start();
        run2.start();
        run3.start();
    }

}

结果:

1796851831
141757079
1721087309

延迟加载显然不是单例,那么如何让延迟加载变成单例呢,可以用synchronized关键字和同步代码块

1,延迟加载同步方案–synchronized关键字
上面的代码不变,
public static MyObject getInstance()->synchronized public static MyObject getInstance()
运行结果显示hashcode值都是一样的,所以这种方法是可行的

2,延迟加载同步方案–同步代码块
public static MyObject getInstance()改为:

    public static MyObject getInstance()  {

        synchronized(MyObject.class) {
            if(myObject != null) {
            }else {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                myObject = new MyObject();
            }

        }
        return myObject;
    }

这样做也可以实现单例,但是这两种方法都有一个问题就是,效率比较低,都把整个方法给锁住了,下面尝试对部分代码同步

3,延迟加载同步方案–针对重要代码单独同步
public static MyObject getInstance()改为:

    public static MyObject getInstance()  {

            if(myObject != null) {
            }else {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //部分代码被上锁,但存在非线程安全问题
                synchronized (MyObject.class) {
                    myObject = new MyObject();
                }

            }
        return myObject;
    }

结果:

1721087309
141757079
1796851831

对部分代码实现同步,运行效率得到了提高,但是单例却实现不了了

4,延迟加载同步方案–使用DCL双检查锁机制
DCL双检查锁机制是实现多线程环境中延迟加载单例设计模式

public class MyObject{

    private volatile static MyObject myObject;
    private MyObject() {

    }
    public static MyObject getInstance()  {

            if(myObject != null) {
            }else { 
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        // TODO 自动生成的 catch 块
                        e.printStackTrace();
                    }

                //部分代码被上锁,但存在非线程安全问题
                synchronized (MyObject.class) {
                    if(myObject ==null) {
                        myObject = new MyObject();
                    }

                }

            }

        return myObject;
    }
}

结果:

141757079
141757079
141757079

实现了单例

4,延迟加载同步方案–静态内部类

之前做一个安卓-RFID读卡器实现打卡的App,当时做到数据获取时,老是拿不到数据,因为当时的程序并不是单例,而用静态内部类可以很好的实现,单例模式

public class Myobject{
    private static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }

    private MyObject() {

    }

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

结果当然是可以实现单例的

5,序列化与反序列化的单例模式实现

静态内置类可以实现线程安全,但是如果遇到了序列化对象,使用默认的方式运行的结果则会是多例
新建MyObject类:

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("111");
        return MyObjectHandler.myObject;
    }*/

}

SaveAndRead类:

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.close();
            fosRef.close();
            System.out.println(myObject.hashCode());
        }catch(FileNotFoundException e) {
            e.printStackTrace();

        }catch(IOException e) {
            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 e1) {
            e1.printStackTrace();
        }catch(ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

这样运行得到的结果不是同一个对象,解决办法就是去掉注释使用readResolve()方法

6,使用static代码块实现单例模式
由于静态代码块是在使用类时就执行了的,所以借助这一特性,可以实现单例

新建MyObject类:

public class MyObject {

    private static MyObject instance = null;
    private MyObject() {}
    static {
        instance = new MyObject();
    }
    public static MyObject getInstance() {
        return instance;
    }
}

测试类:

public class Run extends Thread{

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

    public static void main(String[] args) {
        Run run1 = new Run();
        Run run2 = new Run();
        Run run3 = new Run();
        run1.start();
        run2.start();
        run3.start();
    }
}

结果:

1721087309
1721087309
1721087309
。。。。
。。。。
。。。。

7,使用enum枚举数据类型实现单例模式
新建MyObject类:


public class MyObject {

    private enum MyEnumSingleton{
        INSTANCE;
        private Resource resource;

        private MyEnumSingleton(){
            resource = new Resource();
        }

        public Resource getResource(){
            return resource;
        }
    }

    public static Resource getResource(){
        return MyEnumSingleton.INSTANCE.getResource();
    }
}

测试类:


public class Run {  
    class MyThread extends Thread {  

        @Override  
        public void run() {  
            for (int i = 0; i < 5; i++) {  
                System.out.println(MyObject.getResource().hashCode());  
            }  
        }  
    }  
    public static void main(String[] args) {  
        Run.MyThread t1 = new Run().new MyThread();  
        Run.MyThread t2 = new Run().new MyThread();  
        Run.MyThread t3 = new Run().new MyThread();  

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

    }  

}

结果当然是单例啦

猜你喜欢

转载自blog.csdn.net/qq_37438740/article/details/82013056