java ---- singleton design pattern of resolution instructions Detailed reordering solve reflection attacks, US group face questions

Singleton

The last chapter of the factory model has a detailed description of the design patterns in java and reflect a variety of benefits, it is not something more to say, well, so now let's analyze another design pattern Singleton pattern .

Singleton Pattern definition:

Usage scenarios: to ensure that only one instance absolutely, then what is there in the code in any situation?

ServletContext, ServletConfig (individual profiles), ApplicationContext, DBPool like.

Example 1. Single mode starving formula (early form), with the time of initialization items created, but the disadvantages are also evident, the cost of memory.

/**
 * @Author Darker
 * @Descrption
 * 饿汉式单例,不管用不用,初始化的时候就创建
 * 缺点:耗费内存,以空间换时间,故不存在线程安全问题
 * @Date : Created in 11:31 2020-3-10
 */
public class HungryManSingleTon {

    //初始化就创建
    private static final HungryManSingleTon hungryManSingleTon = new HungryManSingleTon();

    /**
     * 还有一种写法
     *  private static final HungryManSingleTon hungryManSingleTon;
     *  static{
     *      hungryManSingleTon = new HungryManSingleTon();
     *  }
     */

    //构造方法私有
    private HungryManSingleTon(){}

    public static HungryManSingleTon getInstance(){
        return hungryManSingleTon;
    }

    //测试
    public static void main(String[] args) {
        HungryManSingleTon singleTon1 = HungryManSingleTon.getInstance();
        HungryManSingleTon singleTon2 = HungryManSingleTon.getInstance();
        System.out.println("对象1的hashcode"+singleTon1.hashCode()+"////"+"对象1的hashcode"+singleTon2.hashCode());
    }
}

2. singleton lazy man (starving type of upgrade), shortcomings can not be guaranteed when multiple threads to create only one instance.

/**
 * @Author Darker
 * @Descrption
 * 懒汉式单例,等被调用的时候再来创建
 * 优点:优化了内存应用,是饿汉的升级
 * 缺点:存在多线程中被创建多个,以时间换空间,在多线程环境下存在风险
 * @Date : Created in 11:40 2020-3-10
 */
public class SlackerSingleTon {

    //此时不能加final,不然无法改变值
    //加上volatile,禁止指令重排
    private static volatile SlackerSingleTon slackerSingleTon = null;

    //构造方法私有
    private SlackerSingleTon(){}

    public static SlackerSingleTon getInstance(){
        //避免多次重复被覆盖
        if(slackerSingleTon == null){
            //因为现在计算机性能太高,给1秒的停留时间
           try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            slackerSingleTon = new SlackerSingleTon();
        }
        return slackerSingleTon;
    }
    //测试
    public static void main(String[] args) {
        for (int i=0,len=3;i<len;i++){
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"////"+SlackerSingleTon.getInstance().hashCode());
            }).start();
        }
    }
}

Example 3. Single double checks the lock (also in order to ensure a multithreaded singleton).

Well, since the occurrence of an unsafe city line Well, then I do not what it wants to give him a lock, then we become to the above method

public synchronized static SlackerSingleTon getInstance()

This is not to solve the problem of insecurity it.

Sure enough, really solved the problem, but some people still are not picky, it took the synchronized think this heavyweight lock to lock the entire method is not efficient too low, for example.

If this method at a predetermined position more than 400 lines of code, every time the lock had to lock the entire method will undoubtedly reduce a lot of efficiency, then we can think of only locks the assignment of this piece ah!

The code then becomes like this, old iron, no problems ah, the above code ignores the business, efficiency is certainly ok, but run you will find that seemingly did not lock ah. 

Why is this? That is because when we finished judge if not null, then take a lock, by determining if the thread has a lot of empty and have been waiting in front of the lock, if not the outside, so no lock ah!

That being the case, we give the lock which add to null judge.

public static SlackerSingleTon getInstance(){
        //300行业务代码
        //避免多次重复被覆盖
        if(slackerSingleTon == null){
            //100行业务代码
            synchronized (SlackerSingleTon.class){
                //再做一次空判断
                if(slackerSingleTon == null){
                    //因为现在计算机性能太高,给1秒的停留时间
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    slackerSingleTon = new SlackerSingleTon();
                }
            }
        }
        return slackerSingleTon;
    }

ok, so ignoring the business code, but also to ensure efficiency and thread-safe, nice, this is a single case of a double check locks.

but! but! but! ! ! ! The important thing to say three times. 

Do you think this way on the right thing, too naive not, you will always find that the more you learn, the more do not understand, why! The way it is, a small partner in the US group interview, the interviewer let him talk about a single case, and talked about double-checked locking, (pretty easy, I've been a single case in verse, junior partner had in mind), but absolutely Unexpectedly, the interviewer asked is whether the member variables need volatie it? (Nani, volatie, what the hell, junior partner and my heart sank, just know volatie is to ensure the visibility of the atom, what modifications here, so give us a little cool partner).

So that in the end is what kind of problem?

First I went casually points to open the Singleton pattern to explain a few people blog, found a phenomenon.

 Some people before Singleton variable added volatie, while others did not add, this is why, and then write the basic human resolve will be marked thread reordering problem, then the problem came good, the thread will lead to rearrangements what.

Thread reordering can cause any effect?

First we look at this object to a new object when translated into bytecode what it was like (do not know why the students translated into bytecode, you must go to mend the foundation), is actually quite simple, it is this step 5 is constituted.

Now we give you an example:

I have a source

Class T{

 int m = 8;

}

T t = new T();

 Then our assembly code can be so divided.

The first step: new # 2 <T>

Here is equivalent to c ++ inside the new, in fact, allocated memory means is that new memory layout directly out of the object in memory.

Step two: dup (aside, and said the problem did not affect us, interested students themselves about)

The third step: invokespecial # 3 <T. <init >>

Call the class constructor inside.

Step Four: astore_1

This is to associate t on the stack, the stack of t is pointing to the heap of this memory address.

Step five: return

You return the object.

After reading this you will find that we only java wrote a new, five-step assembly code is really done, when it's time to go in the first step only allocate a memory address, no assignment at this time, that is to say At this point i = 0, just gave an initial value, until the third pace with this construction method and only then assigned to the 10 members of the variable i, which means we have a new target when it will have an intermediate state, we call it half initialization.

So if this happens reordering, points to an object pointer to the object of a semi-initialized state, that is, in the figure below points to memory t m is 0, then we come in 2 threads found your object has been pointing to the relationship ( Although only point initialization value), but it will still be used to direct use, so what will happen then? Simply means that you would have to pay 100 dollars, this time happened instruction reordering, I have not executed constructor, assignment yet, put the thread 2 references come away, then you are equal to, or paid only $ 0, a big loss ah old iron!

 This is a single case and we did not half dime ah! And above singleton is nothing, but in multiple threads, if there happens instruction reordering, a thread over it directly to the object of our semi-initialization state to use (the object is to have half the initialization state address), this when not null, directly make use of them, then there is not nothing in the values, then the system is not a problem?

So this must be added volatie, prohibit instruction rearrangement (I will open a blog, explain why your computer instruction rearrangement, volatie solve any of these principles is that this is also a major write singleton).

 

Example 4. The static inner classes single

The above double-checked locking Do not you feel Niubi, but then in order to improve efficiency, we came up with a method, by using the characteristics of the inner class, the underlying execution logic jvm, perfect to avoid the thread-safety issues.

How it works: When an external class loader does not need to be loaded immediately inner class, inner class is not loaded is not initialized INSTANCE, it does not account for memory. When the singleton class is first loaded, the class does not need to be loaded inside a single case, only when the getInstance () method is invoked for the first time, only to initialize the INSTANCE, the first call getInstance () method will cause the virtual machine to load internal singleton class, this method can not only thread-safe, but also to ensure the uniqueness of a single case, but also delay the instantiation of the singleton.
 

First, look at the code

/**
 * @Author Darker
 * @Descrption
 * 静态内部类单例
 * 优点:
 * 全程没有用到sysnchronized
 * 性能最优的一种写法
 * 缺点:
 * 可以被反射攻击,虽然构造方法私有,但反射可以直接获得构造方法
 * @Date : Created in 15:02 2020-3-10
 */
public class ClassSingleTon {
    //私有化构造方法
    private ClassSingleTon(){}

    //可以理解为懒汉式单例
    //LazyHolder里面的逻辑需要等到外部的方法调用时才会执行
    //巧妙的利用了内部类的特性
    //jvm底层执行逻辑,完美避免了线程安全问题
    public static ClassSingleTon getInstance(){
        return LazyHolder.Lazy;
    }
    
    //静态内部类
    private static class LazyHolder{
        private static final ClassSingleTon Lazy = new ClassSingleTon();
    }
    
}

So why do so, not only thread-safe, but also to ensure the uniqueness of a single case, but also delay the instantiation of the singleton.

First, we understand the next class loading mechanism, which is the passage I found in other heavyweights in the blog ( as shown below ), tells us that the machine will only initialize a java class in the five kinds of scenarios, the first time that we come getInstance static method call this class () when this will begin ClassSingleTon class initialization, and then the lazy type is different inside the getInstance () method is not new keyword, but directly returned Lazy static inner classes, so I will not go to initialize the class, that is, initialization always only be called once, so that to achieve a lazy type of effect.

That is how to ensure the security thread of it, I found a passage (in Gangster blog types as shown below ), which means that only one thread to execute when a class is initialized, the other threads are in the blocked state , then this ensures thread safety.

Then such a look, perfect ah, but also lazy, and is thread-safe, but there is a problem, because it is a static inner class initialization, so often unable to pass parameters, such as mass context of this, so in the end double check locks singleton or static inner classes singleton often need to be considered according to the demand.

but! but! but! ! ! I have to use reflection to get your singleton how you do it?

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 15:59 2020-3-10
 */
public class LazyInnerClassSingleTonTest {
    public static void main(String[] args) {
        Class<?> clazz = ClassSingleTon.class;
        try {
            Constructor<?> clazzDeclaredMethod = clazz.getDeclaredConstructor(null);
            clazzDeclaredMethod.setAccessible(true);//强行授权
            Object o1 = clazzDeclaredMethod.newInstance();
            ClassSingleTon o2 = ClassSingleTon.getInstance();
            System.out.println(o1+"////"+o2 );

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Think of it, sample, I do not have your way, my reflection to get your singleton, you can Zaban, this single case and not a single example of it.

We also have the same solution is not.

public class ClassSingleTon {
    //私有化构造方法
    private ClassSingleTon(){
        if(LazyHolder.Lazy != null){
            throw new RuntimeException("不允许创建多个实例,兄弟老老实实按我的规则来搞");
        }
    }

    //可以理解为懒汉式单例
    //LazyHolder里面的逻辑需要等到外部的方法调用时才会执行
    //巧妙的利用了内部类的特性
    //jvm底层执行逻辑,完美避免了线程安全问题
    public static ClassSingleTon getInstance(){
        return LazyHolder.Lazy;
    }

    private static class LazyHolder{
        private static final ClassSingleTon Lazy = new ClassSingleTon();
    }

}

 

ok, get, this is the last example of a single static inner classes.

Of course, there are people say can deserialize, this first sequence singleton object to disk, and then from the disk to read back the object, not the last address are not equal, yes, indeed can do damage, but so far it , the solution of course, there is a method to rewrite inside readResolve directly back to your singleton (it was actually covering it again), friends who are interested can go to research their own look, here I will not engage in the (manual funny).

Example 5. Note single test - single enumeration embodiment, the container singleton

Enumeration singleton

Because there are so Duo Qiqi strange guy is reflected is serialized to destroy a single case, then we naturally find a way to solve not, then think of a single case of the enumeration. Enum in java as in the general category, can have fields and methods, but also create an instance of the enumeration is thread-safe, in any case, it is a singleton.

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 16:30 2020-3-10
 */
public enum EnumSingleTon {
    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void method(){
        //TODO
    }

    public static EnumSingleTon getInstance(){return INSTANCE;}
}

Singleton container (key, spring used)

First, the Code

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 16:51 2020-3-10
 */
public class iocBean {
}

/**
 * @Author Darker
 * @Descrption
 * @Date : Created in 16:46 2020-3-10
 */
public class ContainerSingleTon {
    private ContainerSingleTon(){}

    private static Map<String,Object> ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className){
        
            if(!ioc.containsKey(className)){
                Object obj = null;
                try {
                    Thread.sleep(1000);
                    obj = Class.forName(className).newInstance();
                    ioc.put(className,obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            }
       
        return ioc.get(className);
    }
}

This code See you will find that the container is actually singleton hashMap used to store a plurality of physical objects, and the object to provide you with a simple factory pattern, can of course also use the uniqueness hashMap the like, is formed a lazy single cases, test.

Well, singleton success, then we look at is not thread safe?

Obviously, the result is not secure, then the question comes, spring is obviously not thread-safe, so we quickly realized that certainly locked, so.

public static Object getBean(String className){
        synchronized (ioc){
            if(!ioc.containsKey(className)){
                Object obj = null;
                try {
                    Thread.sleep(1000);
                    obj = Class.forName(className).newInstance();
                    ioc.put(className,obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            }
        }
        return ioc.get(className);
    }

 

Successful, this is a single case of spring-type container species used. Of course, spring there is also a lot of other things, are interested can look at themselves, but thinking about it like this up.
      

Singleton advantages:

1. only one instance in memory, reducing memory overhead.

2. avoid multiple assignment of resources.

3. Set global access point, and strictly control access.

Singleton disadvantages:

1. No interfaces, expansion difficult.

2. If you want to expand singleton object, only to modify the code, there is no other way, inconsistent with the principle of opening and closing.

 

 

Published 27 original articles · won praise 1 · views 3645

Guess you like

Origin blog.csdn.net/qq_40111437/article/details/104770448