Java multithreading study notes - auto increment (decrement) is not an atomic operation

1. Sample code

1. Create a class and run it

        Create a class that inherits from Thread.

package com.algorithm.demo.thread;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程测试类1
 */
public class ExtendsThreadDemo extends Thread {

    private int i = 0;

    @Override
    synchronized public void run()
    {
        super.run();
        for (int k = 0; k < 1000; k++)
        {
            System.out.println("i=" + (++i) + " ThreadName=" + Thread.currentThread().getName());
        }
    }

}

      Instantiate this class, create multiple threads, and then start the threads. The more cpu cores your computer has, please increase the number of for loops below, otherwise it will be less likely to appear.

//实例化
ExtendsThreadDemo demo = new ExtendsThreadDemo();

//创建线程
List<Thread> list = new ArrayList<>();
for(int i=0; i<80; i++)
{
    list.add(new Thread(demo));
}

//运行线程
list.forEach((e) -> {
    e.start();
});

// 让主线程休眠一会,以便子线程彻底执行完,否则可能打印不全
try {
    Thread.sleep(8000);
} catch (InterruptedException e) {
	e.printStackTrace();
}

2. View the running results

        The synchronized keyword here is added to the run method

(1) Results without the synchronized keyword

        The results of multiple runs are inconsistent, and there is a high probability that 80000 will not be printed

i=79994 aaThreadName=Thread-70
i=79995 aaThreadName=Thread-70
i=79996 aaThreadName=Thread-70
i=79997 aaThreadName=Thread-70
i=79998 aaThreadName=Thread-70

(2) The result of adding the synchronized keyword

        The results of multiple runs are consistent

i=79996 aaThreadName=Thread-8
i=79997 aaThreadName=Thread-8
i=79998 aaThreadName=Thread-8
i=79999 aaThreadName=Thread-8
i=80000 aaThreadName=Thread-8

 2. Principle description

1. println() method

        Although the println() method is internally synchronized, the ++i operation occurs before entering println(), so there is a certain probability of non-thread safety problems.

public void println(String x) {
    synchronized (this) {
        newLine();
    }
}

2, atomic manipulation

        The following sentence is an atomic operation

int i = 1;

        Non-atomic operation, i++ is a multi-step operation and can be interrupted. i++ can be divided into 3 steps, the first step reads the value of i, the second step calculates i+1; the third step assigns the final value to i.

i++;

3. CAS operation

        CAS is the abbreviation of Compare and swap. This operation is a hardware-level operation, and the atomicity of the operation is guaranteed at the hardware level. CAS has 3 operands, the memory value V, the old expected value A, and the new value B to be modified. Modify memory value V to B if and only if expected value A and memory value V are the same, otherwise do nothing. Classes in Java sun.misc.Unsafeprovide compareAndSwapIntand compareAndSwapLongseveral other methods to implement CAS.

        In addition, many atomic operation classes based on CAS are provided under the atomic package of jdk, as shown in the following figure:

3. Use AtomicInteger

        Classes that use AtomicInteger do not use the synchronized keyword.

package com.algorithm.demo.thread;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程测试类1
 */
public class ExtendsThreadDemo extends Thread {

    public static AtomicInteger count = new AtomicInteger(0);


    @Override
    public void run()
    {
        super.run();
        for (int k = 0; k < 1000; k++)
        {
            //下面两种方法都可以
            System.out.println("i=" + (count.incrementAndGet()) + " ThreadName=" + Thread.currentThread().getName());
            //System.out.println("i=" + (count.addAndGet(1)) + " ThreadName=" + Thread.currentThread().getName());
        }

    }
}

        run the above class

ExtendsThreadDemo demo = new ExtendsThreadDemo();

List<Thread> list = new ArrayList<>();
for(int i=0; i<80; i++)
{
	list.add(new Thread(demo));
}

list.forEach((e) -> {
	e.start();
});

//让主线程休眠1秒,否则子线程输出不完整。
try {
	Thread.sleep(8000);
} catch (InterruptedException e) {
	e.printStackTrace();
}

        The output results of multiple runs are consistent.

i=79996 ThreadName=Thread-4
i=79997 ThreadName=Thread-4
i=79998 ThreadName=Thread-4
i=79999 ThreadName=Thread-4
i=80000 ThreadName=Thread-4

Fourth, the difference between synchronized and Atomic

        1. Both atomic classes and synchronized can guarantee thread safety, but their implementation principles are different.

        2. Synchronized is a modifier that applies only to methods and blocks but not to variables and classes.

Guess you like

Origin blog.csdn.net/bashendixie5/article/details/123495656