• 《Java多线程编程核心技术》读后感(十四)


     

     

    单例模式与多线程

    立即加载/饿汉模式

    立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接new实例化。

    立即加载/饿汉模式实在调用方法前,实例已经被创建了

    package Six;
    
    public class MyObject {
    
        // 立即加载方式==饿汉模式
        private static MyObject myObject = new MyObject();
    
        private MyObject() {
        }
    
        public static MyObject getInstance() {
            // 此代码版本为立即加载
            // 此版本代码的缺点是不能有其它实例变量
            // 因为getInstance()方法没有同步
            // 所以有可能出现非线程安全问题
            return myObject;
        }
    
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            System.out.println(MyObject.getInstance().hashCode());
        }
    
    }
    package Six;
    
    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();
    
        }
    
    }

    说明对象是同一个,也就实现了立即加载型单例设计模式

     延迟加载/"懒汉模式"

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

    package Six;
    
    public class MyObject {
        
        private static MyObject myObject;
    
        private MyObject() {
        }
    
        public static MyObject getInstance() {
            // 延迟加载
            if (myObject != null) {
            } else {
                myObject = new MyObject();
            }
            return myObject;
        }
        
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            System.out.println(MyObject.getInstance().hashCode());
        }
    }
    package Six;
    
    public class Run {
        
        public static void main(String[] args) {
            MyThread t1 = new MyThread();
            t1.start();
        }
        
    }

    此实验虽然取得一个对象的实例,但如果实在多线程的环境中,就会出现多个实例的情况

     延迟加载/"懒汉模式"缺点

    延迟加载在多线程环境中是错误的。

    package Six;
    
    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;
        }
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            System.out.println(MyObject.getInstance().hashCode());
        }
    
    }
    package Six;
    
    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();
    
        }
    
    }

    打印出了三个对象,并不是单例的,如何解决?见下

    延迟加载/“”懒汉模式“”的解决方案

     (1)声明synchronized关键字

    既然多个线程可以同时进入getInstance()方法,那么只需要对getInstace()方法声明synchronizaed关键字即可

    package Six;
    
    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;
        }
    }

    但此方法的运行效率底下,是同步运行的,下一个线程想要取得对象,则必须等上一个线程释放锁后,才可以继续执行。

    (2)尝试同步代码块

    同步方法是对方法的整体进行持锁,这对运行效率来讲是不利的。等同于上面的写法

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

    (3)针对某些重要的代码进行单独的同步

    同步代码块可以针对某些重要的代码进行单独的同步,而其他的代码则不需要同步,这样在运行时,效率完全可以得到大幅度提升

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

    此方法只对实例化对象的关键代码进行同步,从语句的结构上讲,运行效率得到了提升,但在多线程的环境下还是无法解决得到一个实例对象的结果。

    (4)使用DCl双检查锁机制

    package Six;
    
    public class MyObject {
        
        private 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;
        }
    }

    DCL是大多数多线程结合单例模式使用的解决方案

    使用静态内置类实现单例模式

    其他代码同上

    package Six;
    
    public class MyObject {
        
        // 内部类方式
            private static class MyObjectHandler {
                private static MyObject myObject = new MyObject();
            }
    
            private MyObject() {
            }
    
            public static MyObject getInstance() {
                return MyObjectHandler.myObject;
            }
    
    
    }

    使用staic代码块实现单例模式

    package Six;
    
    public class MyObject {
        
        private static MyObject instance = null;
    
        private MyObject() {
        }
    
        static {
            instance = new MyObject();
        }
    
        public static MyObject getInstance() {
            return instance;
        }
        
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(MyObject.getInstance().hashCode());
            }
        }
    }
    package Six;
    
    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();
    
        }
        
    }

    使用enum枚举数据类型实现单例模式

    package Six;
    
    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:sqlserver://localhost:1079;databaseName=ghydb";
                String username = "sa";
                String password = "";
                String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
                Class.forName(driverName);
                connection = DriverManager.getConnection(url, username, password);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public Connection getConnection() {
            return connection;
        }
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(MyObject.connectionFactory.getConnection()
                        .hashCode());
            }
        }
    }
    package Six;
    
    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();
    
        }
        
    }

     完善使用enum枚举实现单例模式

    前面违反了"职责单一原则”

    package Six;
    
    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:sqlserver://localhost:1079;databaseName=y2";
                    String username = "sa";
                    String password = "";
                    String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
                    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();
        }
    
    }
    package Six;
    
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println(MyObject.getConnection().hashCode());
            }
        }
    }
    package Six;
    
    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();
    
        }
    }

  • 相关阅读:
    IE CSS Bug及解决方案参考手册
    如何学习Javascript
    JS获取当前对象大小以及屏幕分辨率等
    自适应网页设计的方法
    【Javascript Trick系列】二、table相关操作
    移动web开发经验总结
    兼容各种浏览器的常用按钮样式
    获取浏览器的高度和宽度
    让页面弹框垂直水平居中
    Vue is used in projects
  • 原文地址:https://www.cnblogs.com/Michael2397/p/7858287.html
Copyright © 2020-2023  润新知