【Java多线程】编程核心技术第6章:单例模式与多线程(上)

前言:

     本章……很重要,过程中需要思考:如何使单例模式遇到多线程是安全的、正确的!代码也比较多、代码……这个要不要写?写的话、占篇幅有点大

正文:

公用代码:

public class MyThread extends Thread {
    public void run(){
        System.out.println(MyObject2.getInstance().hashCode());
    }
}
public class Run {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start();
    }
}

6.1立即加载/"饿汉模式"

    立即加载:使用类的时候已经将对象创建完毕(如直接new实例化);调用方法的时候,实例已经创建

    该部分主要synchronized和对null的判断解决多线程安全问题,在哪加锁在哪判空很重要,大段代码略过辣

6.2静态内部类实现单例【内部类

/**
 * 序列化:
 *    父类序列化,内部类也要序列化;
 *    静态变量不能被序列化,得到的值一直是最新的值(共享)
 * https://blog.csdn.net/u010335298/article/details/51916582
 * Created by majinxing on 2018/7/30.
 */
public class MyObject2 {

    //内部类方式 世界上独此一份 也不错,静态内部类才可以被序列化
    //遇到序列化对象,还是不行

    /**
     * java对象序列化要求对象all成员都实现序列化接口
     *    so内部类实现序列化而外部封装类没有实现
     *    so对内部序列化:报错,解决:序列化其父类
     * 内部类,local内部类,匿名内部类
     *    实例持有外部封装类实例的隐式引用
     *    可直接访问外部封装类的实例变量和方法
     * 静态嵌套类不能
     */
    private static class MyObjectHandler{
        private static MyObject2 myObject = new MyObject2();
    }
    private MyObject2(){};

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

}

 

2、非静态内部类隐含了一个指向外部类的引用,因此可访问外部类实例的成员

public class InnerClass {

    private int attrib;
    private static int staticAttrib;

    public class Inner1{
        private int a1;
        public int method1(){
            return attrib;
        }
    }
    public static class StaticInner2{
        private int sa1;
        public int method2(){
            return staticAttrib;
        }
    }
    public int method(){
        int a = new Inner1().a1;
        a += new StaticInner2().sa1;
        return a;
    }

    private int foo;
    private int age;
    public class Inner{
        private int foo;
        public int method(){
            //非静态内部类隐含了一个指向外部类引用,
            //可通过 外部类名.this 来在内部类中访问这个引用
            //内 外部 成员重名 通外部类引用访问外部 成员 不重可省引用
            return foo+InnerClass.this.foo+age;
        }
    }

}

java序列化失败的解决

1、修改外部类,外部类实现Serializable接口

2、修改内部类为静态,不会再引用外部类,

6.3序列化与反序列化的单例模式

MyObject

/**
 * 一个类实现了 Serializable接口,就可把它往内存地写再从内存里读出而"组装"成一个跟原来一模一样的对象
 * 遇到单例:从内存中读出而组装的 对象破坏了单例规则,反序列化 新对象克隆出来了=》readResolve 横空出世!
 * Created by Administrator on 2018/7/31.
 */
public class MyObject3 implements Serializable {

    private static final long serialVersionUID = 888L;

    private static class MyObjectHandler {
        private static final MyObject3 myObject = new MyObject3();
    }

    private MyObject3() {
    }

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

    /**
     * 反序列化时直接返回此方法指定的对象,而不需要单独再创建新的对象
     * 当JVM从内存中反序列化地"组装"一个新对象时,会自动调用这readResolve返回我们指定好的对象
     * https://www.jb51.net/article/120538.htm
     */
    protected Object readResolve() throws ObjectStreamException {
        System.out.println("调 readReslove");
        return MyObjectHandler.myObject;
    }
}

业务类:

/**
 * 最后结合的例子不错
 * https://blog.csdn.net/lyb1832567496/article/details/52712218
 * Created by majinxing on 2018/7/31.
 */
public class SaveAndRead {

    public static void main(String[] args) {
        try {
            MyObject3 myObject = MyObject3.getInstance();

            //OutputStream(输出流):输出流是用来写出数据的
            //将数据写入到文件(myObjectFile.txt)中
            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) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            //从文件系统中某个文件中获得输入字节
            //打开一个到实际文件的连接来创建一个 FileInputStream
            FileInputStream fisRef = new FileInputStream(new File(
                    "myObjectFile.txt"));
            ObjectInputStream iosRef = new ObjectInputStream(fisRef);
            MyObject3 myObject = (MyObject3) 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();
        }
    }
}

不是同一个对象,但是、上面的代码块打开,便是同一个对象了,这是为什么呐?看偶滴这一篇 几件关于自定义序列化的小事;

下一篇吧,这篇虽然没说什么,但是已经够长的了~

猜你喜欢

转载自blog.csdn.net/ma15732625261/article/details/81291514