Volatile concept: The main purpose of the volatile keyword is to make a variable visible across multiple threads.
Before you say the volatile keyword, let's look at two small examples
Package com.internet.thread;
public class Runthread extends thread{
private int num = 0;
public void setnum (int num) {
System.out.println (this.num);
This.num = num;
}
public void Run () {
System.out.println (num);
}
public static void Main (string[] args) {
Runthread t1 = new Runthread ();
T1.setnum (ten);
T1.start ();
try {
thread.sleep;
} catch (Interruptedexception e) {
e.printstacktrace ();
}
Runthread t2 = new Runthread ();
T2.setnum (a);
T2.start ();
}
}
As you can see, the two-thread-operation Num has no relationship at all, and each operates individually.
0
0
20
If we add a static modifier in front of num
private static int num = 0;
Run the main method below and the results are as follows, indicating that two threads are working with the same variable num.
0
Ten
20
However, when multiple threads access the same variable a at the same time, a threading problem occurs, as shown in the following figure. For threading problems, we can take a synchronized lock on the variable A, so that no matter how many threads access the variable A, one of the other threads cannot manipulate the variable a while one of the threads is manipulating a, but there is a big problem is that concurrency is too low.
Let's take a look at the following example, the code below
Package com.internet.thread;
public class Volatilethread extends thread{
private Boolean isrunning = true;
private void Setrunning (Boolean isrunning) {
this.isrunning = isrunning;
}
public void Run () {
System.out.println ("Enter the Run Method:");
while (isrunning = = True) {
//:
}
SYSTEM.OUT.PRINTLN ("Thread Stop");
}
public static void Main (string[] args) throws interruptedexception{
volatilethread vt = new Volatilethread ();
Vt.start ();
Thread.Sleep (+);
Vt.setrunning (false);
System.out.println ("The value of isrunning has been set to false");
Thread.Sleep (+);
System.out.println (vt.isrunning);
}
}
The running result, as shown in the following figure, shows that although the value of the isrunning variable becomes false, the while loop is still executing, as shown in the following figure. This is obviously unreasonable.
So why do we change the value of the variable isrunning to false while the while loop does not stop? This is actually caused by the design of the JDK, as shown in the following illustration, the JDK introduces the thread working memory mechanism when designing the thread, and the variable has a copy of the isrunning variable in the main memory, and a duplicate of the variable is stored in the in-line working memory. When the thread executes, the value of the isrunning variable is retrieved from the thread's working memory, and when we set the value of isrunning in the main thread to false, the value of the isrunning variable in main memory has been turned to false, But the value of the isrunning copy in the thread's working memory is still true, so we'll see why the while loop is still running. The purpose of the JDK is to avoid getting the value of the variable each time to get the main memory, because it consumes more performance.
So, how should we solve this problem? In fact, the solution is simple, is to add the volatile keyword modifier, and then rerun the main method, this time found that the while loop is over. isrunning This is the normal running result.
The working mechanism is shown in the following figure. As you can see, when the variable is modified by the volatile keyword, the thread execution engine goes to the main memory to read the value of the variable, while the main memory updates the changed variable value into the thread's working memory.
Modifying a variable with the volatile keyword can make a variable visible across multiple threads, but it does not have atomicity, so let's look at an example that defines a Addcount method that calls count plus 1000 if count is atomic. The final result should be 10000.
Package com.internet.thread;
public class Volatilenoatomic extends thread{
private static volatile int count;
private static void Addcount () {for
(int i=0;i<1000;i++) {
count++;
}
System.out.println (count);
}
public void Run () {
addcount ();
}
public static void Main (string[] args) {
volatilenoatomic[] arr = new VOLATILENOATOMIC[10];
for (int i=0;i<10;i++) {
Arr[i] = new Volatilenoatomic ();
}
for (int i=0;i<10;i++) {
arr[i].start ();}}
}
We run the above code, the result is as follows, we can see that the final result is 8839, not the 10000 we expect, so we can conclude that the variable modified with the volatile keyword is not atomic.
4000
6240
6763
6839
7839
8839
So how do you get the variable count to be atomic? We can use Atomicinteger, as shown in the figure below.
After the modification, we run the main method, the result is as follows, although the intermediate process is not atomic, but the final result must be atomic, the benefit is that multiple threads can be executed simultaneously, intermediate process may have a short data inconsistency, but the final result must be correct. Such examples are also very common, such as our double 11 snapping up goods, so large concurrency, to say that all the data are all at once accurate statistics is impossible, because the concurrency is too large, no time to statistics, and then back to the second, allow short-term data inconsistency, but ultimately must be accurate and consistent data.
4165
4724
6296
7000
8903
9000
10000
Although the volatile keyword has multiple threads of visibility, but does not have synchronization (that is, atomicity), can be counted as a lightweight synchronized, performance is much stronger than synchronized, does not cause blocking (in many open-source architectures, For example, Netty the underlying code on a large number of volatile use, visible netty performance must be very good. Note here: General volatile is used for variable operations that are visible only to multiple threads, and is not a substitute for the synchronized synchronization function. The implementation of atomicity suggests using the atomic class's family of objects to support atomic operations (note that the atomic class only guarantees its own method atomicity and does not guarantee the atomicity of multiple operations)
Let's take an example to illustrate that the atomic class does not guarantee the atomicity of multiple operations, the code is as follows (note that there is no synchronized modification before the Multiadd method)
package com.internet.thread;
Import java.util.ArrayList;
Import java.util.List;
Import Java.util.concurrent.atomic.AtomicInteger;
public class Atomicuse {private static Atomicinteger count = new Atomicinteger (0); Multiple addandget are non-atomic in one method and need to be modified by synchronized to guarantee 4//addandget overall atomicity public int multiadd () {try {Thread.s
Leep (100);
} catch (Exception e) {e.printstacktrace ();
} count.addandget (1);
Count.addandget (2);
Count.addandget (3);
Count.addandget (4);//1+2+3+4=10, that is, to execute a Multiadd method, Count adds a return count.get ();
} public static void Main (string[] args) {final atomicuse au = new Atomicuse ();
list<thread> ts = new arraylist<thread> (); for (int i=0;i<100;i++) {Ts.add (new Thread (new Runnable () {@Override public void run () {System
. Out.println (Au.multiadd ());
}
}));
} for (Thread t:ts) {T.start (); }
}
}
We run the main method, and the result is as follows, if the Multiadd is atomic, then it should be an increase of 10, but we see a number like 223 and 231 in the middle. It is true that the atomic class does not guarantee the atomicity of multiple operations (if only one Addandget method is supported, it is 4, and therefore does not support the atomicity of the method). However, although the Multiadd method is not guaranteed to be atomic, the final result is correct, that is 1000, no matter how many times it runs, there must be 1000, which means that the final is correct.
150-ten-all-in-all
223-
231
260
300
290
280
310
340
321 380 370 390
410
430
420
460 520 510
470 496
480
530
540
551
560
570
596
610
630
606
592
650
640
620
670
690
680 670
740
780
760
770
750
730
730
731
810 830 830 870 870
870
870
890
890
910
950
950
950
950
960
990 970
980
If we want to guarantee the atomicity of the Multiadd method, we'll add the Synchronized keyword to the Multiadd method, as shown in the following figure.
We run the main method again, the result is as follows (because the result is too long, I only intercepted the last segment), you can see that the number count is indeed an increase of 10, until 1000.
830
840
850
860
870
880
890
910
920
930
940
950
960
970
980
990
1000
The volatile keyword is what we learn here.