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();
            }
        }
    

      

  • 相关阅读:
    Good Bye 2014 B. New Year Permutation(floyd )
    hdu 5147 Sequence II (树状数组 求逆序数)
    POJ 1696 Space Ant (极角排序)
    POJ 2398 Toy Storage (叉积判断点和线段的关系)
    hdu 2897 邂逅明下 (简单巴什博弈)
    poj 1410 Intersection (判断线段与矩形相交 判线段相交)
    HDU 3400 Line belt (三分嵌套)
    Codeforces Round #279 (Div. 2) C. Hacking Cypher (大数取余)
    Codeforces Round #179 (Div. 2) B. Yaroslav and Two Strings (容斥原理)
    hdu 1576 A/B (求逆元)
  • 原文地址:https://www.cnblogs.com/silyvin/p/13789575.html
Copyright © 2011-2022 走看看