Java multi-thread synchronized and volatile comparison

Source: Internet
Author: User

Java multi-thread synchronized and volatile comparison
Overview

When performing multi-threaded concurrent processing, you often need to perform visibility access and mutex synchronization operations on resources. Sometimes, we may know from our predecessors that we need to perform volatile or synchronized keyword modification on resources. However, we do not know the difference between the two. We cannot tell when a keyword should be used. This article will discuss this issue.

Introduction to the memory semantic analysis happens-before Model

If you understand the happens-before model literally, you may think that an operation is executed before another operation. However, after learning happens-before, you will not understand it as well. The following is the definition of happens-before in the "Art of Java concurrent programming:

In JMM (Java Memory Model), if the result of an operation needs to be visible to another operation, there must be a happens-before relationship between the two operations. The two operations mentioned here can be within one thread or between different threads.

Memory semantics of volatile

For multi-threaded programming, each thread can have a copy of the variables in the shared memory, which will be discussed later. If a variable is modified by the volatile keyword, write the variable to refresh the copy from the local memory to the shared memory. The reading of this variable is somewhat different, during reading, the local memory copy is ignored, but the data is read from the shared variable.

Synchronized memory Semantics

We say synchronized actually locks the variable. Therefore, both reading and writing are based on the locking operation on this variable. If a variable is modified by the synchronized keyword, write the variable to refresh the copy from the local memory to the shared memory; the read of this variable is to refresh the value in the shared memory to the local memory, and then read data from the local memory. Because the variables are locked throughout the process, other threads cannot read and write the variables. Therefore, it can be understood that any operation on this variable is atomic, that is, the thread is safe.

Instance argument

Some of the above descriptions or definitions may be boring and difficult to understand. Here we will illustrate some examples to compare the details and images.

Volatile visibility test

RunThread. java

public class RunThread extends Thread {    private boolean isRunning = true;    public boolean isRunning() {        return isRunning;    }    public void setRunFlag(boolean flag) {        isRunning = flag;    }    @Override    public void run() {        System.out.println("I'm come in...");        boolean first = true;        while(isRunning) {            if (first) {                System.out.println("I'm in while...");                first = false;            }        }        System.out.println("I'll go out.");    }}

MyRun. java

public class MyRun {    public static void main(String[] args) throws InterruptedException {        RunThread thread = new RunThread();        thread.start();        Thread.sleep(100);        thread.setRunFlag(false);        System.out.println("flag is reseted: " + thread.isRunning());    }}

The above example is just a very common multi-threaded operation. Here we can easily get that the RunThread thread enters an endless loop in the while.
We can see a sentence in the main () method.Thread. sleep (100)Combined with the previously mentioned happens-before memory model, we can see the followingThread. setRunFlag (false)It does not make the while in the happens-before sub-thread. In this way, although the isRunning is modified in the main thread, the while in the sub-thread is not changed, so this will lead to an endless loop in the while.
In this case, the memory model when the thread is working is as follows:

Here, you may wonder why there are two "memory blocks "? This is due to the performance of multithreading. Although the memory allocated by objects and member variables is in the shared memory, each thread can still have a copy of this object. This is done to speed up program execution, this is also a notable feature of modern multi-core processors. From the memory model above, we can see that the Java thread interacts directly with its own working memory (local memory), and the working memory interacts with the shared memory. In this way, a non-atomic operation is formed. It is very dangerous to perform non-atomic operations in a multi-threaded Java environment. We already know this because it may be damaged by asynchronous read/write operations.
Here, the working memory is occupied by the while, so the main thread cannot update the modification to the isRunning variable of the shared memory. Therefore, if we want to break this restriction, we can use the volatile keyword. Use the volatile keyword to modify the while condition variable, that is, isRunning. Modify the RunThread. java code as follows: <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwcmUgY2xhc3M9 "brush: java;"> private volatile boolean isRunning = true;

In this way, volatile modifies the visibility of isRunning so thatThread. setRunFlag (false)The while in the happens-before sub-thread. In the end, the sub-thread jumps out of the while loop and the problem is solved.
Let's take a look at how volatile modifies the visibility of isRunning.

Here, because isRunning is modified by volatile, when the sub-thread wants to access inRunning in the working memory, it is forcibly obtained directly from the shared memory. The isRunning in the shared memory has been modified by the main thread, and has been changed to false, while is broken, so that the thread jumps out of the while loop.

Volatile atomic test

Volatile does have many advantages, but it has a fatal drawback, that is, volatile is not an atomic operation. That is, in the case of multithreading, it is still insecure.
Maybe, at this time, you will ask, since volatile ensures its visibility between threads, it is visible to other threads when to modify it and how to modify it, what a thread reads is a modified value. Why is it still insecure?
Let's illustrate it through an example. This is more vivid. Let's look at the following code:

public class DemoNoProtected {    static class MyThread extends Thread {        static int count = 0;        private static void addCount() {            for (int i = 0; i < 100; i++) {                count++;            }            System.out.println("count = " + count);        }        @Override        public void run() {            addCount();        }    }    public static void main(String[] args) {        MyThread[] threads = new MyThread[100];        for (int i = 0; i < 100; i++) {            threads[i] = new MyThread();        }        for (int i = 0; i < 100; i++) {            threads[i].start();        }    }}
count = 300count = 300count = 300count = 400... ...count = 7618count = 7518count = 9918

This is an unhandled, straightforward process. However, the results are straightforward. In fact, this result is not surprising. From the time we learned Java, we knew that Java's multithreading was not safe. In the above study, do you think this can be solved through the volatile keyword? In this case, let's give it a try and add the volatile keyword to the count variable, as shown below:

public class DemoVolatile {    static class MyThread extends Thread {        static volatile int count = 0;        ... ...    }    public static void main(String[] args) {        ... ...    }}
count = 100count = 300count = 400count = 200... ...count = 9852count = 9752count = 9652... ...count = 8154count = 8054

I don't know if this result will surprise you. It is better to understand the chaotic Number of count. This should happen when multiple threads modify it at the same time. However, we cannot find the logical maximum value "10000" in the result, which is a bit strange. Logically, volatile modifies the visibility of count. For thread A, it is visible to thread B's modification to count. This is not reflected in the results.
Volatile does not guarantee thread security. In the addCount () method in the upper face thread, a Code such as count ++ is executed. The execution process of a Code such as count ++ should be emphasized by the teacher in the first class of learning Java variable auto-increment. Count ++ can be analogous to the following process:

int tmp = count;tmp = tmp + 1;count = tmp;

It can be seen that count ++ is not an atomic operation. Any two threads may separate the above Code, so security is impossible.
So here we know that volatile can change the visibility of variables between threads, but it cannot change the synchronization between threads. Other operations are required for synchronization.

Synchronized synchronization test

As mentioned above, volatile cannot solve the thread security problem because volatile cannot build atomic operations. In multi-thread programming, synchronized is a convenient keyword for synchronous processing. Let's take a look at how synchronized Handles multi-thread synchronization. The Code is as follows:

public class DemoSynchronized {    static class MyThread extends Thread {        static int count = 0;        private synchronized static void addCount() {            for (int i = 0; i < 100; i++) {                count++;            }            System.out.println("count = " + count);        }        @Override        public void run() {            addCount();        }    }    public static void main(String[] args) {        MyThread[] threads = new MyThread[100];        for (int i = 0; i < 100; i++) {            threads[i] = new MyThread();        }        for (int i = 0; i < 100; i++) {            threads[i].start();        }    }}
count = 100count = 200count = 300... ...count = 9800count = 9900count = 10000

With synchronized, we can easily get the desired results. The memory model for the synchronized keyword can be expressed as follows:

When a thread accesses a variable modified by synchronized, it locks the shared memory of the variable. In this case, access by other threads to the variable is mutually exclusive. The internal implementation of synchronized is also the concept of lock.

Ref Java multi-thread programming core technology; Java concurrent programming Art

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.