Thread safety is an issue that must be considered when we do concurrent programming in Java. This class is fine in a single-threaded environment, so can we be sure that it behaves correctly in a multi-threaded concurrent environment?

I am a person, before there is no sideline, dedicated to the work above, so it is quite handy to handle, the state of mind has always maintained a good; But when you have a side hustle, it’s a roller coaster mentality. When the sideline income exceeds the main occupation, people are particularly excited, like playing chicken blood; When the sideline is slow to start, people become worried all day long.

As if I were a single-threaded person, I became particularly insecure when multithreading was on for both my side business and my main business, even though my overall income was much better than before I had my side business.

I haven’t figured out how to make myself feel safe (if you have a good idea, please let me know). But there are three rules for making a class safe in a multithreaded environment. Let me tell you:

1. State variables are not shared between threads. 2. Change the state variable to immutable. 3. Use synchronization when accessing state variables.

So you might ask, what are the state variables?

Let’s start with a class with no state variables.

class Chenmo {
    public void write(a) {
        System.out.println("I have been looking for spring for half my life, and you smile."); }}Copy the code

The Chenmo class is stateless. It has only one method and no member variables or class variables. Any thread that accesses it does not affect the results of another thread, because no state variables are shared between the two threads. It follows that a class without stateless variables must be thread-safe.

Then let’s look at a class with a stateful variable. Suppose silence (Chenmo class) writes a line (write() method) and does a statistic so that it can ask publishers for money. We add a statistical field to the Chenmo class as shown in the following code example.

class Chenmo {
    private long count = 0;
    public void write(a) {
        System.out.println("I have been looking for spring for half my life, and you smile."); count++; }}Copy the code

The Chenmo class can count trips accurately in a single-threaded environment, but not in a multithreaded environment. Because increment count++ can be split into three operations: read count, increment count by one, and assign the result to count. With multiple threads, the timing of these three operations can be chaotic, resulting in a smaller count than expected.

PS: Look back to the previous section on Concurrent Programming in Java (PART 1) : Get ready.

Writing isn’t easy. We can’t afford to be silent, can we? Then do something about it.

If thread A is modifying the count variable, it is necessary to prevent thread B or C from using this variable so that thread B or C can use count in the same state as thread A.

How to prevent it? You can add the synchronized keyword to the write() method. An example of this code is shown below.

class Chenmo {
    private long count = 0;
    public synchronized void write(a) {
        System.out.println("I have been looking for spring for half my life, and you smile."); count++; }}Copy the code

The keyword synchronized is one of the simplest synchronization mechanisms, ensuring that only one thread can execute write() at a time, thus ensuring that count++ is safe in multi-threaded environments.

When writing concurrent applications, it is important to keep in mind that the code should first work correctly and then improve the performance of the code.

However, as is known to all, synchronized is expensive, and the access to write() method between multiple threads is mutually exclusive. Thread B must wait for the end of thread A’s access when accessing, which cannot reflect the core value of multithreading.

Java. Util. Concurrent. Atomic. AtomicInteger is an atomic operation of Integer type, it provides the add and subtract operations are thread-safe. We can then modify the Chenmo class as shown in the following code example.

class Chenmo {
    private AtomicInteger count = new AtomicInteger(0);
    public void write(a) {
        System.out.println("I have been looking for spring for half my life, and you smile."); count.incrementAndGet(); }}Copy the code

The write() method no longer needs the synchronized keyword, so it is no longer necessary to call the method in the mutually exclusive way between multiple threads, which can improve the efficiency of statistics to a certain extent.

One day, the publisher changed the way it counted not just lines but words, and the Chenmo class needed to add another member variable. An example of this code is shown below.

class Chenmo {
    private AtomicInteger lineCount = new AtomicInteger(0);
    private AtomicInteger wordCount = new AtomicInteger(0);
    public void write(a) {
        String words = "In all my life I have walked many roads, crossed many Bridges, seen many clouds, drunk many kinds of wine, but I have loved only one man of my age."; System.out.println(words); lineCount.incrementAndGet(); wordCount.addAndGet(words.length()); }}Copy the code

Do you think this code is thread-safe?

It turns out that this code is not thread-safe. Because lineCount and wordCount are two variables, even though they are thread-safe, when thread A does lineCount plus 1, There is no guarantee that thread B will start lineCount increment after thread A completes the wordCount count.

What to do? The method is also very simple, with the following code example.

class Chenmo {
    private int lineCount = 0;
    private int wordCount = 0;
    public void write(a) {
        String words = "In all my life I have walked many roads, crossed many Bridges, seen many clouds, drunk many kinds of wine, but I have loved only one man of my age.";
        System.out.println(words);
        
        synchronized (this) { lineCount++; wordCount++; }}}Copy the code

Lock the line count (lineCount++) and word count (wordCount++) code to ensure that these two lines of code are atomic. In other words, thread B must wait for thread A to finish collecting statistics.

synchronized (lock) {… } is a simple built-in locking mechanism provided by Java to ensure the atomicity of code blocks. A thread automatically acquires the lock before entering the locked block of code and releases the lock when exiting the block, ensuring that a set of statements is executed as an indivisible unit.


Previous: Concurrent Programming in Java (I) : Introduction

How to Guarantee atomicity of shared variables?

Search the public account “Silent King ii” on wechat, and reply to “Free video” to obtain 500G high-quality teaching video (classified).