zoukankan      html  css  js  c++  java
  • 类加载的并发,单例模式,静态资源加载

    全限定类在同一个类加载器只能加载一次,意味着static对象及代码块只一次,为单例之依据

    如果并发发生,则阻塞

     故类的加载不存在多线程,因为只执行一次,其他线程等着加载线程,由jvm来保证线程安全性

    public class ByLoad {
    
        static {
            try {
                System.out.println(Thread.currentThread() + " start " + System.currentTimeMillis()/1000 );
                Thread.sleep(20000);  【11行】
                System.out.println(Thread.currentThread() + " end " + System.currentTimeMillis()/1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void func() {
            System.out.println(Thread.currentThread() + " func " + System.currentTimeMillis()/1000);
        }
    }
    
    public class Main {
        public static void main(String []f) {
            CountDownLatch countDownLatch = new CountDownLatch(1);
            for(int i=0; i<3; ++i) {
                int id = i+1;
                Thread thread = new Thread(new MyThread(countDownLatch), "thread - " + id + " haha");
                thread.start();
            }
            countDownLatch.countDown();
    
        }
    
        private static class MyThread implements Runnable {
    
            private CountDownLatch countDownLatch;
    
            public MyThread(CountDownLatch countDownLatch) {
                this.countDownLatch = countDownLatch;
            }
    
            @Override
            public void run() {
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ByLoad.func(); 【35行】
            }
        }
    }
    

    输出:

    Thread[thread - 1 haha,5,main] start 1602256385
    Thread[thread - 1 haha,5,main] end 1602256405
    Thread[thread - 1 haha,5,main] func 1602256405
    Thread[thread - 3 haha,5,main] func 1602256405
    Thread[thread - 2 haha,5,main] func 1602256405

    可以看到线程1执行到ByLoad.func()时,进行ByLoad的加载,静态代码块执行

    2 3 线程的ByLoad.func()被阻塞

    "thread - 3 haha" #12 prio=5 os_prio=31 tid=0x00007f820199a000 nid=0x4003 in Object.wait() [0x0000700002cf2000]
    java.lang.Thread.State: RUNNABLE
    at Thread.loader.Main$MyThread.run(Main.java:35)
    at java.lang.Thread.run(Thread.java:745)

    "thread - 2 haha" #11 prio=5 os_prio=31 tid=0x00007f820003d800 nid=0x4103 in Object.wait() [0x0000700002bef000]
    java.lang.Thread.State: RUNNABLE
    at Thread.loader.Main$MyThread.run(Main.java:35)
    at java.lang.Thread.run(Thread.java:745)

    "thread - 1 haha" #10 prio=5 os_prio=31 tid=0x00007f8201837800 nid=0x4203 waiting on condition [0x0000700002aeb000]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at Thread.loader.ByLoad.<clinit>(ByLoad.java:11)
    at Thread.loader.Main$MyThread.run(Main.java:35)
    at java.lang.Thread.run(Thread.java:745)

    3 我们来回看单例模式

    饿汉式

    1
    2
    3
    4
    5
    6
    7
    public class ImageLoader{  
         private static ImageLoader instance = new ImageLoader;  
         private ImageLoader(){}  
         public static ImageLoader getInstance(){   
              return instance;   
          }  
    }

    一上来就把单例对象创建出来了,要用的时候直接返回即可

    这里的“一上来”是有误区的,java的类本来就是懒加载,不存在一上来;

    那么为什么还会出来后面的静态代码块式单例模式?

    试想一下这种情况:

    public class ImageLoader{  
         private static ImageLoader instance = new ImageLoader;  
         private ImageLoader(){}  
         public static ImageLoader getInstance(){   
              return instance;   
          }  
      public static void any() {...}
    }

    any() 先于getInstance执行,则不算懒加载了

    故:

    为了避免意外提前加载,在实际业务代码中才引入静态内部类加固懒加载,而不完全指望公开类懒加载,

    单例的静态内部类模式即是如此原理

    /

    6 我们常常碰到静态基础数据加载:

    6.1 双检

    public class CurrencyDataProvider implements DataProvider<String> {
    
        @Inject
        private SCEFCacheUtils scefCacheUtils;
    
        private static List<String> result;
    
        @Override
        public Collection<String> getAll() {
            if (result == null) {
                synchronized (CurrencyDataProvider.class) {
                    if (result == null) {
                        result = new ArrayList<>(scefCacheUtils.getCurrencyList());
                        Collections.sort(result);
                    }
                }
            }
            return result;
        }
    }
    

      

    比较好理解,然后sonar抵触double-check lock

    6.2 

    public class CurrencyDataProvider implements DataProvider<String> {
    
        @Inject
        private SCEFCacheUtils scefCacheUtils;
    
        private static List<String> result;
    
        @Override
        public Collection<String> getAll() {
            if (result == null) {
                result = new ArrayList<>(scefCacheUtils.getCurrencyList());
                Collections.sort(result);
            }
            return result;
        }
    }
    

      

    那就拆锁吧,首次访问并发时无法多查几次库,毕竟是读操作

    然后被sonar指出,sort(result)有并发访问的风险-大bug

    6.3

    public class CurrencyDataProvider implements DataProvider<String> {
    
        @Inject
        private SCEFCacheUtils scefCacheUtils;
    
        private static List<String> result;
    
        @Override
        public Collection<String> getAll() {
            if (result == null) {
                List<String> list = new ArrayList<>(scefCacheUtils.getCurrencyList());
                Collections.sort(list);
                result = list;
            }
            return result;
        }
    }
    

      

    好我们改一下,让局部对象参与sort,搞定了再给类对象,并发时,最多是多次对result赋值

    然而sonar又不干了,认为static result被并发了,非线程安全

    6.4

    public class CurrencyDataProvider implements DataProvider<String> {
    
        @Override
        public Collection<String> getAll() {
            return InnerLoader.res;
        }
    
        private static class InnerLoader {
            private static List<String> res;
    
            static {
                Injector injector = CRFGuiceContext.getInjector();
                SCEFCacheUtils scefCacheUtils1 = injector.getInstance(SCEFCacheUtils.class);
                List<String> list = new ArrayList<>(scefCacheUtils1.getCurrencyList());
                Collections.sort(list);
                res = list;
            }
        }
    }
    

    静态内部类-sonar推荐的方案

    6.4.1 使用static代码块进行static资源加载,因为第1点指出static代码块和对象只会搞一次,这样就避免了并发重复加载,且替代了double-check

    6.4.2 为啥不直接写在CurrencyDataProvider的static代码块?

    因为第4点指出,如果CurrencyDataProvider被意外loadclass或forname或其他方式率先加载了,执行static代码块时,Guice环境可能还没好

      

    6.5

    public class CurrencyDataProvider implements DataProvider<String> {
    
        @Override
        public Collection<String> getAll() {
            return InnerLoader.res;
        }
    
        private static class InnerLoader {
            private static List<String> res;
    
            static {
                Injector injector = CRFGuiceContext.getInjector();
                SCEFCacheUtils scefCacheUtils1 = injector.getInstance(SCEFCacheUtils.class);
                res = new ArrayList<>(scefCacheUtils1.getCurrencyList());
                Collections.sort(res);
            }
        }
    }
    

      

    有了第1点的结论:类的加载不存在多线程,因为只执行一次,其他线程等着加载线程,我们放心的对static对象res直接进行sort,而不用局部对象缓冲,因为不会有第2个线程再执行到这儿了,只此一次执行

    7

    mybatis guice 事务代理切面  中sqlSessionManager也可用:

    //    private static SqlSessionManager sqlSessionManager = null;
    
        private SqlSessionManager getOdsSqlSessionManager() {
            return SqlManagerLoader.sqlSessionManager;
        }
    
        private static class SqlManagerLoader {
            private static SqlSessionManager sqlSessionManager = null;
            static {
                OdsTransactionMapper odsTransactionMapper = getBeanFromFactoryGuice(OdsTransactionMapper.class);
                InvocationHandler lvInvHandler0 = Proxy.getInvocationHandler(odsTransactionMapper);
                ManagedMapperProvider managedMapperProvider0 = (ManagedMapperProvider) lvInvHandler0;
                sqlSessionManager = managedMapperProvider0.getSqlSessionManager();
            }
        }
    

      

  • 相关阅读:
    hibernate-取消关联外键引用数据丢失抛异常的设置@NotFound
    css-画三角箭头
    tomcat提示警告: An attempt was made to authenticate the locked user"tomcat"
    liunx下tomcat启动 Cannot find ./catalina.sh
    java:提示Could not initialize class sun.awt.X11GraphicsEnvironment
    MySQL定时器开启、调用实现代码
    mysql-存储过程案例-存储过程中创建表和修改表数据
    PowerDesigner导出SQL时自动生成注释
    mysql-利润set变量模拟分组查询每组中的第N条数据
    HTTP 405 错误 – 方法不被允许 (Method not allowed)
  • 原文地址:https://www.cnblogs.com/silyvin/p/13789575.html
Copyright © 2011-2022 走看看