• 线程安全(2)--多线程逸出分析和正确处理


    多线程溢出写法:

    public class ThisEscape {

        public ThisEscape(EventSource source) {
            source.registerListener(new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            });

        }

    }

    点评:加粗的这一段隐式this事件线程已经暴露给ThisEscape构造函数,而构造函数在该类中首先被其它类调用。因此整个this都暴露了。仅仅要其它线程在ThisEscape未构造之前(构造返回状态)调用这个类,那么this就会被新建线程共享并识别它(线程溢出)。

    因此正确的写法:

    public class ThisEscape{
        private final EventListener listener;
        private ThisEscape() {
            listener = new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            };
        }
        public static ThisEscape newInstance(EventSource source) {
            ThisEscape safe = new ThisEscape();
            source.registerListener(safe.listener);
            return safe;
        }

    点评:利用工厂模式来规避EventListener线程溢出,新建的线程无法在构造函数之前共享和识别safe,从而保证线程安全。

    我们在做jdbc连接一般也是採用工厂模式,但非常少考虑是否存在线程溢出的现象,应尽量避免使用静态块,因此须要引起足够的重视。

    案例分析:

    public class ConnectionPool {
        private Vector<Connection> pool;
        private String url="jdbc:mysql://localhost:3306/xxx";
        private String username="root";
        private String password="root";
        private String driverClassName="com.mysql.jdbc.Driver";

        /**
         * 连接池的大小,连接数
         */
        private int poolSize=10;
        
        private static ConnectionPool instance =null;
        /**
         * 私有的构造方法。禁止外部创建本类的对象,要想获得本类的对,通过getIstance方法
         * 使用了设计模式中的单子模式
         */
        private ConnectionPool(){
            init();
        }
        private void init(){
            pool =new Vector<Connection>(poolSize);
            //readConfig();
            addConnection();
        }

        public synchronized void release(Connection conn){
            pool.add(conn);
        }
        /**
         * 关闭连接池中全部数据库的连接
         */
        public synchronized void closePool(){
            for(int i=0;i<pool.size();i++){
                try{
                    ((Connection)pool.get(i)).close();
                }catch(SQLException e){
                    e.printStackTrace();
                }
                pool.remove(i);
            }
        }
        /**
         * 返回当前连接池中的一个对象
         */
        public static ConnectionPool getInstance(){
            if(instance==null){
                instance=new ConnectionPool();
            }
            return instance;
        }

        /**
         * 返回连接池中的一个数据库连接
         */
        public synchronized Connection getConnection(){
            if(pool.size()>0){
                Connection conn=pool.get(0);
                pool.remove(conn);
                return conn;
            }else{
                return null;
            }
        }
        private void addConnection(){
            Connection conn=null;
            for(int i=0;i<poolSize;i++){
                try{
                    Class.forName(driverClassName);
                    conn=DriverManager.getConnection(url,username,password);
                    pool.add(conn);
                }catch(ClassNotFoundException e){
                    e.printStackTrace();
                }catch(SQLException r){
                    r.printStackTrace();
                }
            }
        }
        private void readConfig(){
            try{
                String path=System.getProperty("use.dir")+"\dbpool.properties";
                FileInputStream is=new FileInputStream(path);
                Properties props=new Properties();
                props.load(is);
                this.driverClassName=props.getProperty("driverClassName");
                this.username=props.getProperty("username");
                this.password=props.getProperty("password");
                this.url=props.getProperty("url");
                this.poolSize=Integer.parseInt(props.getProperty("poolSize"));
            }catch(Exception e){
                e.printStackTrace();
                System.out.println("读取属性文件错误");
            }
        }

    点评:这个连接池算是比較健全了,但还是有不足的地方。看下标记的绿色的部分应加上final,橙色加粗部分应加上volatile

  • 相关阅读:
    12C 中,发生脑裂时,节点保留策略
    如何修改集群的公网信息(包括 VIP)
    从 ASH 找到消耗 PGA 和 临时表空间 较多的 Top SQL_ID
    Oracle SCN详解
    10046 trace
    使用trace文件定位ORA-00060问题
    (转)计算机漏洞安全相关的概念POC 、EXP 、VUL 、CVE 、0DAY
    PowerShell 相关常用命令(update...)
    (转)主从同步常遇见问题处理-线上MYSQL同步报错故障处理总结
    pentestbox 安装后的基本设置
  • 原文地址:https://www.cnblogs.com/clnchanpin/p/7149589.html
Copyright © 2020-2023  润新知