Java secure coding guide: double detection of locks

brief introduction

Double detection locking mode is a design mode. We reduce the cost of acquiring locks by detecting locking conditions for the first time rather than actually acquiring locks.

The double check lock mode usage is typically used to implement the singleton factory mode that performs delayed initialization. Delayed initialization delays the construction of member fields or objects referenced by member fields until they are actually created.

However, we need to be very careful to use the double detection mode to avoid sending errors.

Delayed loading in singleton mode

Let's first look at a single instance mode that works normally in a single thread:

public class Book {

    private static Book book;

    public static Book getBook(){
        if(book==null){
            book = new Book();
        }
        return book;
    }
}

The above class defines a getbook method to return a new book object. Before returning the object, we first judge whether the book is empty. If not, we will create a new book object.

At first glance, there seems to be no problem. Let's consider it carefully:

Book = new book() is actually a complex command, not an atomic operation. It can be roughly divided into 1. Allocating memory, 2. Instantiating objects, and 3. Associating objects with memory addresses.

In a multithreaded environment, we may get unexpected results due to the impact of reordering.

The easiest way is to add the synchronized keyword:

public class Book {

    private static Book book;

    public synchronized static Book getBook(){
        if(book==null){
            book = new Book();
        }
        return book;
    }
}

Double check mode

What should I do if I want to use the double check mode?

public class BookDLC {
    private static BookDLC bookDLC;

    public static BookDLC getBookDLC(){
        if(bookDLC == null ){
            synchronized (BookDLC.class){
                if(bookDLC ==null){
                    bookDLC=new BookDLC();
                }
            }
        }
        return bookDLC;
    }
}

We first judge whether the bookdlc is empty. If it is empty, it means that a new object needs to be instantiated. At this time, we lock the bookdlc Class, and then judge whether it is empty again. If it is not empty this time, initialize it.

So is there a problem with the code on?

Yes, although bookdlc is a static variable, due to CPU caching, we can't guarantee that the bookdlc after the current thread is assigned will be visible to other threads immediately.

Therefore, we need to define bookdlc as volatile, as follows:

public class BookDLC {
    private volatile static BookDLC bookDLC;

    public static BookDLC getBookDLC(){
        if(bookDLC == null ){
            synchronized (BookDLC.class){
                if(bookDLC ==null){
                    bookDLC=new BookDLC();
                }
            }
        }
        return bookDLC;
    }
}

Implementation of static domain

public class BookStatic {
    private static BookStatic bookStatic= new BookStatic();

    public static BookStatic getBookStatic(){
        return bookStatic;
    }
}

The JVM will perform static initialization after the class is loaded and before it is used by threads, and a lock will be obtained in this initialization stage, so as to ensure that the memory write operation will be visible to all threads in the static initialization stage.

The above example defines the static variable, which will be instantiated in the static initialization phase. This method is called early initialization.

Next, let's look at a mode of delaying the initialization of the placeholder class:


public class BookStaticLazy {

    private static class BookStaticHolder{
        private static BookStaticLazy bookStatic= new BookStaticLazy();
    }

    public static BookStaticLazy getBookStatic(){
        return BookStaticHolder.bookStatic;
    }
}

In the above class, the class is initialized only when the getbookstatic method is called.

ThreadLocal version

We know that ThreadLocal is the local variable of thread, which is actually the member variable ThreadLocal in thread Encapsulation of threadlocalmap.

All data stored in ThreadLocal is actually stored in ThreadLocal, a member variable of the current thread Threadlocalmap.

If ThreadLocal is used, we can first judge whether there is ThreadLocal in the current thread, and then create it if not.

As follows:

public class BookThreadLocal {
    private static final ThreadLocal<BookThreadLocal> perThreadInstance =
            new ThreadLocal<>();
    private static BookThreadLocal bookThreadLocal;

    public static BookThreadLocal getBook(){
        if (perThreadInstance.get() == null) {
            createBook();
        }
        return bookThreadLocal;
    }

    private static synchronized void createBook(){
        if (bookThreadLocal == null) {
            bookThreadLocal = new BookThreadLocal();
        }
        perThreadInstance.set(bookThreadLocal);
    }
}

Code for this article:

learn-java-base-9-to-20/tree/master/security

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>