Understanding volatile via example -- Reference

Source: Internet
Author: User

We have spent last couple of months stabilizing the lock detection functionality in plumbr. during this we have stumbled into pair tricky concurrency issues. attributes of the issues are unique, but one participant type of issues keeps repeatedly appearing.

You might have guessed it-Misuse ofVolatileKeyword. we have detected and solved bunch of issues where the extensive usage of volatile made arbitrary parts of the application slower, extended locks holding time and eventually bringing the JVM to its knees. or vice versa-granting too liberal access policy has triggered some nasty concurrency issues.

I guess every Java developer recallthe first steps in the language. days and days spent with manuals and tutorials. Those tutorials all had the list of keywords, among whichVolatileWas one of the scariest. As days passed and more code was written without the need for this keyword, pointer of us forgot the existenceVolatile. Until the production systems started either corrupting data or dying in unpredictable manner. debugging such cases forced some of us to actually understand the concept. but I bet it was not a pleasant lesson to have, so maybe I can save some of you some time by shedding light upon the concept via a simple example.

Example of volatile in action

The example is simulating a bank office. the type of bank office where you pick a queue number from a ticketing machine and then wait for the invite when the queue in front of you has been processed. to simulate such office, we have created the following example, consisting of two threads.

First of the two threads is implementedCustomerinline.This is a thread doing nothing but waiting until the value inNext_in_lineMatches customer's ticket. Ticket number is hardcoded to be #4. When the time arrives (Next_in_line> = 4), the threadAnnounces that the waiting is over and finishes. This simulates a customer arriving to the office with some MERs already in queue.

The queuing implementation is inQueueClass which runs a loop calling for the next customer and then simulating work with the customer by sleeping 200 ms for each customer. after calling the next customer, the value stored in class variableNext_in_lineIs increased by one.

public class Volatility {    static int NEXT_IN_LINE = 0;    public static void main(String[] args) throws Exception {        new CustomerInLine().start();        new Queue().start();    }    static class CustomerInLine extends Thread {        @Override        public void run() {            while (true) {                if (NEXT_IN_LINE >= 4) {                    break;                }            }            System.out.format("Great, finally #%d was called, now it is my turn\n",NEXT_IN_LINE);        }    }    static class Queue extends Thread {        @Override        public void run() {            while (NEXT_IN_LINE < 11) {                System.out.format("Calling for the customer #%d\n", NEXT_IN_LINE++);                try {                    Thread.sleep(200);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

So, when running this simple program you might expect CT the output of the program being similar to the following:

Calling for the customer #1Calling for the customer #2Calling for the customer #3Calling for the customer #4Great, finally #4 was called, now it is my turnCalling for the customer #5Calling for the customer #6Calling for the customer #7Calling for the customer #8Calling for the customer #9Calling for the customer #10

As it appears, the assumption is wrong. Instead, you will seeQueueProcessing through the list of 10 MERs and the hapless thread simulating customer #4 never alerts it has seen the invite. what happened and why is the customer still sitting there waiting endlessly?

Analyzing the outcome

What you are facing here is a JIT optimization applied to the Code caching the access toNext_in_lineVariable. Both threads get their own local copy andCustomerinlineThread never seesQueueActually increasing the value of the thread. if you now think this is some kind of horrible bug in the JVM then you are not fully correct-compilers are allowed to do this to avoid rereading the value each time. so you gain a performance boost, but at a cost-if other threads change the state, the thread caching the copy does not know it and operates using the outdated value.

This is precisely the caseVolatile. With this keyword in place, the compiler is warned that a Participating State is volatile and the code is forced to reread the value each time when the loop is executed. equipped with this knowledge, we have a simple fix in place-just change the declaration ofNext_in_lineTo the following and your MERs will not be left sitting in queue forever:

static volatile int NEXT_IN_LINE = 0;

For those, who are happy with just understanding the use caseVolatile, You are good to go. Just be aware of the extra cost attached-when you start declaring everything to beVolatileYou are forcing the CPU to forget about local caches and to go straight into main memory, slowing down your code and clogging the memory bus.

Volatile under the hood

For those who wish to understand the issue in more details, stay with me. to see what is happening underneath, lets turn on the debugging to see the assembly code generated from the bytecode by the JIT. this is achieved by specifying the following JVM options:

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Running the program with those options turned on bothVolatileTurned on and off, gives us the following important insight:

Running the codeWithoutVolatileKeyword, Shows us that on instruction between we have comparison between two values. When comparison fails we continue through 0x00000001085c160to define which jumps back to 0x00000001085c160and an infinite loop is born.

  0x00000001085c1c56: mov    0x70(%r10),%r11d  0x00000001085c1c5a: cmp    $0x4,%r11d  0x00000001085c1c5e: jge    0x00000001085c1c68  ; OopMap{off=64}                                                ;*if_icmplt                                                ; - Volatility$CustomerInLine::[email protected] (line 14)  0x00000001085c1c60: test   %eax,-0x1c6ac66(%rip)        # 0x0000000106957000                                                ;*if_icmplt                                                ; - Volatility$CustomerInLine::[email protected] (line 14)                                                ;   {poll}  0x00000001085c1c66: jmp    0x00000001085c1c60  ;*getstatic NEXT_IN_LINE                                                ; - Volatility$CustomerInLine::[email protected] (line 14)  0x00000001085c1c68: mov    $0xffffff86,%esi

WithVolatileKeyword in place, we can see that on instruction 0x000000010a5c1c40 we load value to a register, on 0x000000010a5c1c4a compare it to our guard value of 4. if comparison fails, we jump back from 0x000000010a5c1c4e to 0x000000010a5c1c40, loading value again for the new check. this ensures that we will see changed valueNext_in_lineVariable.

  0x000000010a5c1c36: data32 nopw 0x0(%rax,%rax,1)  0x000000010a5c1c40: mov    0x70(%r10),%r8d    ; OopMap{r10=Oop off=68}                                                ;*if_icmplt                                                ; - Volatility$CustomerInLine::[email protected] (line 14)  0x000000010a5c1c44: test   %eax,-0x1c1cc4a(%rip)        # 0x00000001089a5000                                                ;   {poll}  0x000000010a5c1c4a: cmp    $0x4,%r8d  0x000000010a5c1c4e: jl     0x000000010a5c1c40  ;*if_icmplt                                                ; - Volatility$CustomerInLine::[email protected] (line 14)  0x000000010a5c1c50: mov    $0x15,%esi

Now, hopefully the explanation will save you from couple of nasty bugs.

Original article: https://plumbr.eu/blog/understanding-volatile-via-example

 

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.