Thread security can be very complex, and in the absence of sufficient synchronization, the order in which operations are performed in multiple threads is unpredictable and can even produce strange results (unexpected). The plus method of the Tools tool class below will add one to the count, and for convenience, NUM and plus () are static:
Public class Tools { privatestaticint num = 0; Public Static int Plus () { num++ ; return num; }}
Let's write a task that calls the plus () method and outputs the count:
Public class Implements Runnable { @Override publicvoid run () { int num = Tools.plus (); SYSTEM.OUT.PRINTLN (num); }}
Finally create 10 threads to drive the task:
Public class Main { publicstaticvoid main (string[] args) { for ( int i = 0; I < 10; i++) { new Thread (new Task ()). Start (); }}
Output:
24315678910
Everything looks normal, and 10 threads execute concurrently, resulting in a 0 cumulative 10-time result. We changed 10 times to 10,000 times:
Public class Main { publicstaticvoid main (string[] args) { for ( int i = 0; I < 10000; i++) { new Thread (new Task ()). Start (); }}
Output:
... 99949995999699979998
On my computer, this program only occasionally output 10000, why?
The problem is that if the timing is not right, then two threads will get the same value when calling the plus () method, num++ appears to be a single operation, but in fact contains three operations: reads NUM, adds num One, and writes the result to Num. Because the runtime may alternately perform operations between multiple threads, these threads may perform read operations at the same time so that they get the same value and add 1 to the result that the same value is returned in different thread invocations.
A thread: num=9→→→9+1=10→→→num=10B Thread: →→→→num=9→→→9+1=10→→→num=10
If you take this action in a different way, it will look clearer, Num plus one assigns a temp variable, tmp, sleeps one second, and finally assigns TMP to NUM:
Public classTools {Private Static intnum = 0; Public Static intPlus () {intTMP = num + 1; Try{Thread.Sleep (1000); } Catch(interruptedexception e) {e.printstacktrace (); } num=tmp; returnnum; }}
This time we start with two threads to see the problem:
Public class Main { publicstaticvoid main (string[] args) { for ( int i = 0; I < 2; i++) { new Thread (new Task ()). Start (); }}
After starting the program, the console outputs after 1s:
11
A thread: num=0→→→0+1=1→→→num=1B Thread: →num=0→→→0+1=1→→→num=1
The above example is a common concurrency security problem, called a race condition (Race Condition), in a multithreaded environment, whether plus () returns a unique value, depending on how the runtime alternates between operations on the thread, which is not what we want to see.
Because multiple threads share the same memory address space and are running concurrently, they may be able to access or modify the variables that other threads are using, and the thread can get an error due to unpredictable data changes. To make the behavior of multithreaded procedures predictable, access to shared variables must be coordinated so that there is no interference between threads. Fortunately, Java provides a variety of synchronization mechanisms to coordinate such access.
Modify the plus () to a synchronous method, and at the same time only one thread can enter the method to fix the error:
Public classTools {Private Static intnum = 0; Public synchronized Static intPlus () {intTMP = num + 1; Try{Thread.Sleep (1000); } Catch(interruptedexception e) {e.printstacktrace (); } num=tmp; returnnum; }}
Console output:
12
If you change the plus () method to num++ and drive 10,000 threads to execute, you can also ensure that you can output to 10000 each time.
In addition to security, multi-threaded threads can also have active problems (deadlocks, etc.), performance issues (context switching, etc.), which we will follow in detail.
Thread Safety: Why does the num++ operation also go wrong?