zoukankan      html  css  js  c++  java
  • 《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();
    
        }
    }

  • 相关阅读:
    eclipse中的Invalid text string (xxx).
    在jsp文件中出现Unknown tag (c:out)
    eclipse 界面复原
    ecilpse 纠错插件
    Multiple annotations found at this line:- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    Port 8080 required by Tomcat v9.0 Server at localhost is already in use. The server may already be running in another process, or a system process may be using the port.
    调用第三方https接口
    调用第三方http接口
    创建带值枚举
    spring整合redis之Redis配置文件
  • 原文地址:https://www.cnblogs.com/Michael2397/p/7858287.html
Copyright © 2011-2022 走看看