Let's talk about volatile and javavolatile in java.

Source: Internet
Author: User

Let's talk about volatile and javavolatile in java.

Memory visibility

Pay attention to compound operations

Solve the atomic problem of num ++ operations

Disable Command Re-sorting

Summary

Memory visibility

Volatile is a lightweight synchronization mechanism provided by Java. It also plays an important role in concurrent programming. Compared with synchronized (synchronized is usually called a heavyweight lock), volatile is more lightweight. Compared with the huge overhead of thread context switching caused by synchronized, if volatile can be properly used, naturally, it is a matter of beauty.

In order to have a clear and thorough understanding of volatile, we will analyze it step by step. First, let's take a look at the following code:

Public class TestVolatile {boolean status = false;/*** switch to true */public void changeStatus () {status = true;}/*** if the status is true, running. */Public void run () {if (status) {System. out. println ("running ....");}}}

In the preceding exampleMulti-threaded EnvironmentIf thread A executes the changeStatus () method and thread B runs the run () method, can the output "running..." be guaranteed?

  The answer is NO!

  This conclusion may be confusing and understandable. Because if the changeStatus method is run in the single-threaded model and the run method is executed, the "running .... ", but in a multi-threaded model, this guarantee cannot be achieved. For the shared variable status, the modification of thread A is "invisible" for thread B. That is to say, thread B may not be able to observe that the status has been changed to true. So what is visibility?

  The so-called visibility means that when a thread modifies the value of the shared variable, the new value can be immediately known to other threads. Obviously, there is no way to achieve memory visibility in the above example.

Java Memory Model

  Why is this happening? First, let's take a look.JMM (java Memory Model)

 Java Virtual Machine has its own Memory Model (Java Memory Model, JMM). JMM can eliminate Memory access differences between various hardware and operating systems, to achieve consistent memory access performance for java programs on various platforms.

JMM determines when a thread writes shared variables to another thread. JMM defines the abstract relationship between the thread and the Main Memory: Shared variables are stored in the Main Memory, each thread has a private Local Memory. The Local Memory stores copies of the primary Memory used by this thread, all operations on variables by the thread must be performed in the working memory, rather than directly reading and writing the variables in the main memory. The interaction between the three is as follows:

 

It should be noted that JMM is an abstract memory model, so the so-called local memory, the main memory is an abstract concept, not necessarily corresponds to the actual cpu cache and physical memory. Of course, if it is for the purpose of understanding, it is also impossible to match.

  After learning about the simple definition of JMM, the problem is easy to understand. For common shared variables, such as the status in the preceding section, thread A changes it to true. This action occurs in the local memory of thread A, and is not synchronized to the main memory yet. Thread B caches the initial value of status, false, at this time, the status value may not be changed, so the above problem occurs. How can this shared variable be invisible in the multi-threaded model? The rough method is actually locking, but synchronized or Lock is too heavyweight here, which means it is a bit of a gun. A reasonable method is volatile.

Volatile has two features. The first is to ensure the visibility of shared variables on all threads. When a shared variable is declared as volatile, the following effects are returned:

1. When a volatile variable is written, JMM forcibly refreshes the variables in the local memory corresponding to the thread to the main memory;

2. This write operation will invalidate the cache in other threads.

In the above example, you only need to declare the status as volatile to ensure that when thread A changes it to true, thread B can immediately learn

 volatile boolean status = false;
Pay attention to compound operations

However, we have been comparing volatile and synchronized, just because these two keywords share some memory semantics, volatile cannot completely replace synchronized, it is still a lightweight lock, and volatile is not competent in many scenarios. Let's take a look at this example:

Package test; import java. util. concurrent. countDownLatch;/*** Created by chengxiao on. */public class Counter {public static volatile int num = 0; // use CountDownLatch to wait for the computing thread to finish executing static CountDownLatch countDownLatch = new CountDownLatch (30 ); public static void main (String [] args) throws InterruptedException {// enable 30 threads for the accumulate operation (int I = 0; I <30; I ++) {new Thread () {public void run () {for (int j = 0; j <10000; j ++) {num ++; // auto-increment operation} countDownLatch. countDown ();}}. start ();} // wait until the computing thread finishes executing countDownLatch. await (); System. out. println (num );}}

Execution result:

224291

For this example, some may wonder if the shared variable modified with volatile can ensure visibility. Shouldn't the result be 300000?

The problem lies in the num ++ operation,Because num ++ is not an atomic operation, it is a composite operation.. We can simply explain that this operation is composed of three steps:

1. Read

2. Add one

3. assign values

Therefore, in A multi-threaded environment, it is possible that thread A reads num to the local memory. At this time, other threads may have increased the number of threads, thread A still auto-adds the expired num and re-writes it to the primary storage, resulting in the unexpected result of num, but less than 30000.

Solve the atomic problem of num ++ operations

For num ++ composite class operations, you can use the atomic operation class in java concurrent packet sending to ensure its atomicity through the cyclic CAS method.

/*** Created by chengxiao on. */public class Counter {
// Use the atomic operation class public static AtomicInteger num = new AtomicInteger (0); // use CountDownLatch to wait for the computing thread to finish executing static CountDownLatch countDownLatch = new CountDownLatch (30 ); public static void main (String [] args) throws InterruptedException {// enable 30 threads for the accumulate operation (int I = 0; I <30; I ++) {new Thread () {public void run () {for (int j = 0; j <10000; j ++) {num. incrementAndGet (); // atomic num ++, through the CAS method} countDownLatch. countDown ();}}. start ();} // wait until the computing thread finishes executing countDownLatch. await (); System. out. println (num );}}

Execution result

300000

The basic principles of atomic operations are described in the following sections.

Disable Command Re-sorting

Volatile also has the following features:Disable Command Re-sorting optimization.

Reordering is a method by which the compiler and processor sort the instruction sequence to optimize program performance. However, some rules need to be followed for the re-sorting:

1. The reordering operation does not resort operations with data dependencies.

For example, a = 1; B = a; this command sequence is not reordered at compile time and when the processor is running because the second operation depends on the first operation.

2. The re-sorting aims to optimize performance. However, no matter how re-sorting is performed, the execution results of programs in a single thread cannot be changed.

    For example, a = 1; B = 2; c = a + B, the first step (a = 1) and the Second Step (B = 2) because there is no data dependency, re-sorting may occur, but the c = a + B operation will not be reordered, because the final result must be c = a + B = 3.

In the single-thread mode, the final result will be correct. However, in the multi-threaded environment, the problem arises. Here is an example, we slightly improved the first TestVolatile example and added a shared variable.

Public class TestVolatile {int a = 1; boolean status = false;/*** switch to true */public void changeStatus () {a = 2; // 1 status = true; // 2}/*** if the status is true, running. */Public void run () {if (status) {// 3 int B = a + 1; // 4 System. out. println (B );}}}

If thread A executes changeStatus and thread B executes run, can we ensure that thread B is equal to 3 at 4?

  The answer is still not guaranteed!It is also possible that B is still 2. As we mentioned above, in order to provide program concurrency, the compiler and the processor may re-sort the commands. In the preceding example, 1 and 2 do not have data dependencies, it may be re-ordered. Execute status = true and then execute a = 2. At this time, thread B will smoothly reach 4 places, and the = 2 operation in thread a has not been executed, so the result of B = a + 1 may still be equal to 2.

You can use the volatile keyword to modify shared variables to disable this sort.If shared variables are modified with volatile, memory barrier will be inserted into the instruction sequence during compilation to prevent specific types of processors from being reordered.

There are also rules for the volatile prohibition Command Re-sorting, which are briefly described as follows:

  1. When the second operation is voaltile write, no matter what the first operation is, it cannot be reordered.

2. When a local operation is volatile reading, no matter what the second operation is, it cannot be reordered.

3. When the first operation is volatile write, and the second operation is volatile read, it cannot be reordered.

Summary:

To sum up, volatile is a lightweight synchronization mechanism with two main features: one is to ensure the visibility of shared variables on all threads, and the other is to disable command re-sorting optimization. At the same time, it should be noted that volatile is atomic for reading/writing a single shared variable, but volatile cannot guarantee its atomicity for composite operations such as num ++, of course, the solution is also proposed in this article, that is, to use the atomic operation class in the package and ensure the atomicity of num ++ operations through the cyclic CAS method. The atomic operation class will be introduced in subsequent articles.

 

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.