Home  >  Article  >  Java  >  How to solve thread safety issues in Java singleton pattern?

How to solve thread safety issues in Java singleton pattern?

王林
王林forward
2023-05-09 19:28:061474browse

    1. Factors to consider when using multi-threading

    Improving efficiency:Using multi-threading is to make full use of the CPU resources, improve task efficiency
    Thread safety:The most basic thing about using multi-threading is to ensure thread safety

    So when we design multi-threaded code, we must Improve the efficiency of task execution as much as possible under the premise of meeting thread safety. ##Consider thread safety:

    There is no security issue in code that does not operate shared variablesTo read shared variables, use volatile to modify the variablesWriting to shared variables , use synchronized locking

    2. Singleton mode

    The singleton mode can ensure that there is only one instance of a certain class in the program, and not Create multiple instances
    For example: DataSource (data connection pool), a database only needs one connection pool object

    The singleton mode is divided into hungry mode and lazy mode


    1. Hungry mode

    Hungry mode is to create an instance when the class is loaded

    This method is thread-safe (JVM uses locking internally, that is, multiple threads Call the static method, only one thread competes for the lock and completes the creation, only executed once)

    Implementation code:

    public class Singleton {
        private static Singleton instance = new Singleton();
        private Singleton(){
     
        }
        public static Singleton getInstance(){
            return instance;
        }
    }

    2. Lazy mode

    The lazy mode does not create an instance when the class is loaded, and only creates it when it is used for the first time.

    Implementation code:
    public class Singleton {
        private static Singleton instance = null;
        private Singleton(){
     
        }
        public static Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    Observe the above code, under single thread There is no thread safety issue, but is there a security issue in a multi-threaded environment?

    Analysis:

    When the instance is not created, if multiple threads call the getInstance method, multiple instances may be created, and there will be Thread safety issues
    But once the instance is created, thread safety issues will not occur when subsequent threads call the getInstance method

    Result:
    Thread safety issues occur when the instance is first created

    3. Lazy mode (improved using synchronized) We use synchronized modification, ????‍????️The code is as follows:

    public class Singleton {
        private static Singleton instance = null;
        private Singleton(){
     
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    }

    What are the problems with implementing thread safety in this way?

    Analysis:

    We use synchronized modification on the method, that is, every time the method is called, the lock will be competed, but the instance only needs to be created once. , that is, after creating the instance, calling this method still requires competing for the lock to release the lock

    Result:Although it is thread-safe, the efficiency is low

    4. Lazy mode (improved by using double check lock) Make changes based on the above code:

    Use double if judgment to reduce the frequency of competition locks

    Use volatile to modify instance

    Implementation code:

    public class Singleton {
        private static volatile Singleton instance = null;
        private Singleton(){
     
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){ //外层的if判断:如果实例被创建直接return,不让线程再继续竞争锁
                //在没有创建实例时,多个线程已经进入if判断了
                //一个线程竞争到锁,其他线程阻塞等待
                synchronized (Singleton.class) {
                    //内层的if判断,目的是让竞争失败的锁如果再次竞争成功的话判断实例是否被创建,创建释放锁return,没有则创建
                    if(instance == null){ 
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    Analysis of double if:

    Outer layer if judgment: The instance is only created once. When the instance has been created, no subsequent operations are required. Return directly.

    Inner if judgment:

    When the instance is not created, multiple threads compete for the lock at the same time. , only one thread succeeds in competition and creates an instance, and other threads that fail in competition will block and wait. When the first thread releases the lock, these threads that failed in competition will continue to compete, but the instance has already been created, so you need to perform the if again. Judgment
    Drawing analysis, as shown below:

    3. The principle of volatile

    How to solve thread safety issues in Java singleton pattern?volatile ensures visibility At the Java level, volatile is a lock-free operation. Multiple threads can read volatile-modified variables concurrently and execute them in parallel, which is almost as efficient as lock-free execution.

    In volatile-modified variables, The CPU uses the cache consistency protocol to ensure that the latest main memory data is read

    Cache consistency: If another thread modifies the variable modified by volatile, the variable in the CPU cache will be reset. is invalid, to operate this variable, you must re-read it from the main memory

    4. Volatile expansion issues (understand)

    If volatile does not guarantee ordering Is there any problem with the way double check lock is written?

    About the new object, it is divided into 3 instructions in order:

    (1) Allocate the memory space of the object

    (2) Instantiate the object

    ( 3) Assign a value to a variable

    The normal execution order is (1)(2)(3). The JVM may optimize and reorder the order to (1)(3)(2)

    The result of this reordering may cause the assignment to be completed before the object is instantiated after the memory space is allocated.
    After this wrong assignment, instance==null is not established, and the thread will hold For an instance that has not been instantiated, an error will occur when using its properties and methods.

    After using volatile to ensure orderliness:

    The thread does not care about the new object (1 )(2)(3) What is the order? The instance obtained by the subsequent thread is the instance that has been instantiated.
    In the CPU, there is a CPU-level locking mechanism based on volatile variable operations (it guarantees (1)(2) (3) After all execution, write it back to the main memory, and then execute other threads’ operations on the variable)

    The above is the detailed content of How to solve thread safety issues in Java singleton pattern?. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete