An article to read Java concurrency and thread safety

Source: Internet
Author: User
Tags volatile

First, preface

For a long time, have been trying to analyze the nature of Java thread security, but some micro-point to want to not understand, it was shelved down, the previous period slowly to understand, then put all the dots in series, while thinking clear, finishing into such an article.

Second, guidance

1. Why are there multiple threads?

2. What is the nature of the thread safety description?

3. Java memory Model (JMM) Data visibility issues, command reordering, memory barriers

Iii. announcement of the answer

1. Why are there multiple threads

When it comes to multithreading, it's easy to equate with high performance, but not so, for example, from 1 to 100, four threads are not necessarily faster than a thread. Because of thread creation and context switching, this is a huge overhead.

So what is the purpose of designing multithreading? To see a practical example, the computer usually needs to interact with people, assuming that the computer has only one thread, and this thread is waiting for the user's input, then in the process of waiting, the CPU can not do anything, only wait, resulting in low CPU utilization. If it is designed to be multi-threaded, it can be cut to other threads while the CPU is waiting for resources, increasing CPU utilization.

Modern processors mostly contain multiple CPU cores, so for large-scale computing tasks, it can be disassembled into multiple small tasks concurrently by multi-threaded methods to improve the efficiency of computation.

Summed up nothing more than two points, improve CPU utilization, improve computational efficiency.

2. The nature of thread safety

Let's first look at an example:

PublicClassADD {Privateint count =0;Public Static void Main(String[] args){Countdownlatch Countdownlatch =New Countdownlatch (4); Add add =New Add (); Add.doadd (Countdownlatch);try {countdownlatch.await ();}catch (Interruptedexception e) {e.printstacktrace ();} System.Out.println (Add.getcount ());}Public void Doadd(Countdownlatch Countdownlatch){for (int i =0; I <4; i++) {New Thread (New Runnable () {Public void Run ( { Span class= "Hljs-keyword" >for (int j = 0; J < 25; J + +) {count++;} Countdownlatch.countdown ();}}). Start ();}} public  int  getCount  ( { Span class= "Hljs-keyword" >return Count;}}            

The above is an example of adding a variable 100 times, except that it uses 4 threads, each thread increases 25 times, executes with 4 threads such as Countdownlatch, and prints out the final result. In fact, we want the result of the program to be 100, but the printed result is not always 100.

This leads to a thread-safe description of the problem, and we first describe the thread safety in layman's words:

Thread safety is about getting the program to run the results we want, or to say, let the program execute as we see it.

Explain what I summed up this sentence, we first new out an Add object, called the object's Doadd method, originally we want each thread to increment 25 times sequentially, finally get the correct result. This object is thread-safe if the program is increased to run as we pre-set it.

Let's take a look at Brian Goetz's description of thread safety: When multithreading accesses an object, it does not require additional synchronization, or any other coordinated operation on the caller, without regard to the scheduling and alternation of these threads in the runtime environment. This object is thread-safe when the object's behavior is called to get the correct result.

Let's analyze why this code doesn't ensure that it always gets the right results.

3. Java memory Model (JMM) Data visibility issues, command reordering, memory barriers

First of all, from the computer's hardware efficiency, CPU computing speed is several orders of magnitude faster than memory, in order to balance the CPU and memory contradictions, the introduction of cache, each CPU has cache, or even multi-level cache L1, L2 and L3, then cache and memory interaction requires cache consistency Protocol, This is not an in-depth explanation. The final processor, cache, and main memory interactions are as follows:

Then the Java memory model (Java memory model, abbreviated JMM) also defines the relationships between threads, working memory, and main memory, very similar to the hardware definition.

Here, incidentally, the partitioning of memory in Java Virtual runtime

Method Area: Storage class information, constants, static variables, etc., shared by each thread

Virtual machine Stack: Each method is executed to create a stack frame for storing local variables, operand stacks, dynamic links, etc., the virtual machine stack primarily stores this information, thread private

Local method Stack: Native method services used by virtual machines, such as C programs, etc., thread private

Program Counter: Log program to run to which line, equivalent to the current thread byte code line number counter, thread private

Heap: new instance objects are stored in this area and are the main battlefield of GC, thread sharing.

So for JMM definition of the main memory, most of the time can be corresponding to the heap memory, method area, such as thread-sharing area, here is only conceptually corresponding, in fact, the program counters, virtual machine stack and so on is also part of the main memory, the specific look at the virtual machine design.

Well, to understand the JMM memory model, let's analyze why the above program didn't get the right results. See, Thread A, b simultaneously to read the main memory of the count initial value in the respective working memory, while performing a self-increment operation, write back to the main memory, and finally get the wrong result.

Let's take a closer look at the essential causes of this error:

(1), visibility, the latest value of working memory does not know when it will be written back to main memory

(2), order, the thread must be an orderly access to shared variables, we use the concept of "horizon" to describe the process, in the perspective of the B-thread, when he saw a thread operation, after the value is written back to the memory, immediately read the latest value to do the operation. A thread should also see that the B operation is finished, read it right away, do the arithmetic, and get the correct result.

Next, let's make a concrete analysis of why we need to limit the visibility and order of the two aspects.

By adding the volatile keyword to count, visibility is guaranteed.

private volatile int count = 0;

The volatile keyword will add the lock prefix to the final compiled instruction, and the lock prefix command does three things

(1), prevent order reordering (here the analysis of this problem is not important, will be detailed later)

(2), lock the bus or use lock cache to ensure the atomicity of execution, early processing may be used to lock the bus, so that other processors can not access the memory through the bus, the cost is relatively large, now the processor is locked cache in the way, in conjunction with cache consistency to resolve.

(3), write all the buffer data back to the main memory, and ensure that the other processor cache the variable is invalid

Since the guarantee of visibility, plus the volatile keyword, why still can't get the correct result, because count++, not atomic operation, count++ equivalent to the following steps:

(1), read count assignment from main memory to the thread replica variable:

Temp=count

(2), thread copy variable plus 1

Temp=temp+1

(3), thread copy variables write back to main memory

Count=temp

Even if it is really harsh to the bus lock, resulting in the same moment, only one processor access to the count variable, but in the step (2) operation, the other CPU can already access the count variable, the latest operation results have not been brushed back to the main memory, resulting in incorrect results, so must be guaranteed order.

The nature of the order, then, is to ensure that only one CPU at a time can execute the critical section code. This is usually a lock-up, the nature of the lock is divided into two kinds: pessimistic lock and optimistic lock. such as the typical pessimistic lock synchronized, JUC package under the typical optimistic lock reentrantlock.

To sum up: to ensure thread safety, you must guarantee two points: the visibility of shared variables, the order of critical section code access.

Article Source: https://my.oschina.net/u/1778239/blog/1610185

Reference Tutorial: http://www.roncoo.com/course/view/b6f89747a8284f44838b2c4da6c8677b

An article to read Java concurrency and thread safety

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.