Java safe coding guide: thread safety rules

Introduction

If we introduce shared variables in multithreading, then we need to consider the issue of thread safety under multithreading. So what thread safety issues should we pay attention to in the process of writing code?

Let's take a look.

Pay attention to the rewriting of thread-safe methods

Everyone has done method rewriting. We know that method rewriting does not check method modifiers. In other words, we can rewrite a synchronized method into a non-thread-safe method:

public class SafeA {
    
    
    public synchronized void doSomething(){
    
    
    }
}
public class UnsafeB extends SafeA{
    
    
    @Override
    public void doSomething(){
    
    
    }
}

When we implement subclass functions, we must maintain the thread safety of the method.

Overflow of this in the constructor

What is this? According to the JLS specification, when used as the main expression, the keyword this represents a value, which is a reference to the object on which the instance method is called or the object being constructed.

So here comes the problem, because this can represent the object being constructed, which means that if the object has not been constructed yet and this can be accessed externally, it will cause the problem of external objects accessing the object that has not been successfully constructed.

Let's take a look at what happens to this overflow:

public class ChildUnsafe1 {
    
    

    public static ChildUnsafe1 childUnsafe1;
    int age;

    ChildUnsafe1(int age){
    
    
        childUnsafe1 = this;
        this.age = age;
    }
}

The above is a very simple this overflow situation. In the process of constructing the function, assigning this to a public object will cause this to be accessed by other objects before it is initialized.

Then can we adjust the order?

public class ChildUnsafe2 {
    
    

    public static ChildUnsafe2 childUnsafe2;
    int age;

    ChildUnsafe2(int age){
    
    
        this.age = age;
        childUnsafe2 = this;
    }
}

As we saw above, the assignment of this is placed at the end of the construction method. Is it possible to avoid accessing uninitialized objects?

The answer is no, because java reorders the code, so the position of childUnsafe2 = this is uncertain.

We need to modify it like this:

public class Childsafe2 {
    
    

    public volatile static Childsafe2 childUnsafe2;
    int age;

    Childsafe2(int age){
    
    
        this.age = age;
        childUnsafe2 = this;
    }
}

Add a volatile descriptor to prohibit reordering, a perfect solution.

Let's look at the problem of a parent-child class, or Childsafe2 above, and we will write a subclass for it:

public class ChildUnsafe3 extends Childsafe2{
    
    

    private Object obj;

    ChildUnsafe3(int age){
    
    
       super(10);
       obj= new Object();
    }

    public void doSomething(){
    
    
        System.out.println(obj.toString());
    }
}

What's wrong with the above example? Because the parent class has exposed the this variable when calling the constructor, it may cause the external program to call doSomething() when the obj in ChildUnsafe3 has not been initialized. At this time, obj has not been initialized, so it will Throw NullPointerException.

The solution is to not set this in the constructor. We can create a new method and set it after the constructor is called.

Do not use background threads when the class is initialized

If a background process is used during class initialization, it may cause deadlock. We consider the following situation:

public final class ChildFactory {
    
    
    private static int age;

    static {
    
    
        Thread ageInitializerThread = new Thread(()->{
    
    
            System.out.println("in thread running");
            age=10;
        });

        ageInitializerThread.start();
        try {
    
    
            ageInitializerThread.join();
        } catch (InterruptedException ie) {
    
    
            throw new AssertionError(ie);
        }
    }

    public static int getAge() {
    
    
        if (age == 0) {
    
    
            throw new IllegalStateException("Error initializing age");
        }
        return age;
    }

    public static void main(String[] args) {
    
    
        int age = getAge();
    }
}

The above class uses a static block. In this block, we start a background process to set the age field.

In order to ensure visibility, static variables must be initialized before other threads run, so ageInitializerThread needs to wait for the static variables of the main thread to execute before it can run, but we call the ageInitializerThread.join() method, and the main thread needs to wait in turn The execution of ageInitializerThread is complete.

Eventually led to a loop waiting, causing a deadlock.

The simplest solution is not to use background processes, directly set in the static block:

public final class ChildFactory2 {
    
    
    private static int age;

    static {
    
    
            System.out.println("in thread running");
            age=10;
    }

    public static int getAge() {
    
    
        if (age == 0) {
    
    
            throw new IllegalStateException("Error initializing age");
        }
        return age;
    }

    public static void main(String[] args) {
    
    
        int age = getAge();
    }
}

Another way is to use ThreadLocal to save the initialization variables in the thread local.

public final class ChildFactory3 {
    
    

    private static final ThreadLocal<Integer> ageHolder = ThreadLocal.withInitial(() -> 10);

    public static int getAge() {
    
    
        int localAge = ageHolder.get();
        if (localAge == 0) {
    
    
            throw new IllegalStateException("Error initializing age");
        }
        return localAge;
    }

    public static void main(String[] args) {
    
    
        int age = getAge();
    }
}

The code of this article:

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

This article has been included in http://www.flydean.com/java-security-code-line-threadsafe/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to discover!

Welcome to pay attention to my official account: "Program those things", know the technology, know you better!

Guess you like

Origin blog.csdn.net/superfjj/article/details/109235604