java thread safety issues

Posted by khendar on Thu, 16 Dec 2021 01:50:09 +0100

Critical resources

A critical resource is a shared resource that is allowed to be used by only one process at a time. Each process is mutually exclusive, and the shared resources are called critical resources. Hardware belonging to critical resources include, printers, tape drives, etc;

The software includes message queue, variable, array, buffer, etc. Mutual exclusion is adopted among processes to realize the sharing of this resource.

Race condition

When two threads compete for the same resource, if they are sensitive to the access order of the resource, it is said that there is a race condition.

The code region that leads to race conditions is called the critical region. Using appropriate synchronization operations in the critical region can avoid race conditions, such as using synchronized or locking mechanisms.

Thread safety

Code that allows multiple threads to execute simultaneously is called thread safe code. Thread safe code does not contain race conditions.

Examples of thread safety problems:

When multiple threads operate on a variable at the same time, it may cause dirty reading and writing of variables (similar to mysql)

package com.company;

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        //Create 20 threads
        for (int i =1;i<=20;i++){
            new Thread(()->{
                for (int j =1;j<=1000;j++){
                    test.incA();
                }
            },"test"+i).start();
        }
        while(Thread.activeCount() > 2){ //main, gc, indicating that there are other threads executing
            Thread.yield();//Thread comity
        }
        System.out.println(Thread.currentThread().getName() + "\t int Type number Final value:" + test.a());
    }
}

class Test{
    public int a;

    public int a(){
        return a;
    }

    public void incA(){
        a++;
    }
}

Execution results:

/Users/tioncico/Library/Java/JavaVirtualMachines/openjdk-14.0.1/Contents/Home/bin/java -javaagent:/Applications/IDE/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=56786:/Applications/IDE/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/tioncico/IdeaProjects/test/out/production/untitled104 com.company.Main
main	 int Type number Final value: 19893

You can see that there are 20 threads * 1000 increments, but the actual value is less than 20000. This situation belongs to non thread safety

How to achieve thread safety?

volatile keyword

Through the volatile modification attribute, this attribute will directly modify the memory without internal thread caching and reordering

volatile keyword can guarantee the visibility and order of attribute operations, but it cannot guarantee atomicity

visibility

Refers to the visibility between threads. The modified state of one thread is visible to another thread. That is, when one thread modifies a shared variable, another thread can see it immediately. For example, variables modified with volatile will have visibility.

Order

Orderliness refers to the sequential execution of programs in a single threaded environment

In a multithreaded environment, program execution may be out of order due to instruction rearrangement

Instruction rearrangement

Instruction rearrangement means that the compiler and CPU may rearrange the instructions for performance

Atomicity

Sub - nature means that an operation is non - interruptible Even when multiple threads execute together,

Once an operation starts, it will not be disturbed by other threads

volatile visibility case:

package com.company;

import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        //Create 1 thread
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "\t Executing");

            try {
                TimeUnit.SECONDS.sleep(3);//Allow time for the main thread code to execute
                test.setA(100);
                System.out.println(Thread.currentThread().getName() + "\t int The value of type is:" + test.a());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"demonstration").start();
        while(test.a==0){//If it is always 0, it will always cycle

        }
        System.out.println(Thread.currentThread().getName() + "\t int Type number The value is:" + test.a());
    }
}

class Test{
    public int a=0;

    public int a(){
        return a;
    }

    public void incA(){
        a++;
    }
    public void setA(int a){
        this.a = a;
    }
}

Since there is no volatile keyword, the value obtained by the main thread is 0, so the loop will not be interrupted

Add volatile keyword:

class Test{
    public volatile int a=0;

    public int a(){
        return a;
    }

    public void incA(){
        a++;
    }
    public void setA(int a){
        this.a = a;
    }
}

volatile cannot solve atomicity problems:

The main reasons are:

Thread 1 gets the value of a=0, and 0 + + becomes 1 But in fact, at the same time, threads 1-20 get the value of a=0, and + + becomes 1, which will lead to thread write coverage, and finally the value will be less than 20000;

AtomicIntegrer atomic class

Although volatile cannot achieve atomicity, it can be implemented through Java util. concurrent. Atomicinteger class} saves data and implements atomic operations:

class Test{
    public AtomicInteger a = new AtomicInteger();

    public int a(){
        return a.get();
    }

    public void incA(){
        a.getAndIncrement();
    }
}

result:

/Users/tioncico/Library/Java/JavaVirtualMachines/openjdk-14.0.1/Contents/Home/bin/java -javaagent:/Applications/IDE/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62725:/Applications/IDE/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/tioncico/IdeaProjects/test/out/production/untitled104 com.company.Main
main	 int Type number Final value: 20000

synchronized keyword

synchronized keyword can lock a method so that it can only be accessed by one thread at a time:

class Test {
    public int a;

    public int a() {
        return a;
    }

    public synchronized void incA() {
        a++;
    }
}

Operation results:

Reference: https://blog.csdn.net/weixin_41947378/article/details/112245369

This article is an original article of xianshike. You don't need to contact me for reprint, but please indicate that it comes from xianshike blog www.php20.com cn