Dry: Java Multithreading details (enclosed source code)

Source: Internet
Author: User
Tags volatile


A thread is the smallest unit of program execution, and multithreading means that a program can have multiple execution units running at the same time (this is related to your CPU core).

Opening a new thread in Java is very simple, creating a thread object, and then invoking its Start method, a new thread is turned on.

So where does the execution code go? There are two ways of doing this: 1. When creating a thread object, make a copy of its run method and put the execution code in the Run method. 2. When creating the thread object, pass it a runnable object and place the execution code in the Run method of the Runnable object.

If multithreaded operations are different resources, the threads do not interact with each other and do not cause any problems. However, if multiple threads operate on the same resource (shared variable), there will be multi-threaded conflicts, knowing the reason for these conflicts, we should first understand the Java memory Model (JMM).

I. Java memory model (JMM)

1.1 Java Memory Model (JMM) Introduction

The Java memory model determines when a thread's write to a shared variable is visible to another thread. From the point of view of sampling: Shared variables between threads are stored in main memory, each of which has a private local memory (local memory), where the thread is stored as a copy of the read/write shared variable.

There are two kinds of memory: main memory and thread local memory, and a copy of the shared variable is placed in local memory when the thread starts.

A thread-to-shared variable operation is actually a copy variable in the operating thread's local memory, and when the replica variable changes, the thread flushes it into main memory (not necessarily immediately refreshed, and when the flush is controlled by the thread itself).

When a variable in the main memory changes, it notifies the other thread that the cache row of the variable is invalidated, so that when other threads read the variable from local memory, the variable is found to be invalid, and it is re-read from memory.

1.2 Visibility

From the above introduction, we see the multi-threaded operation shared variables, there is a problem, that is the visibility problem: that one thread to the shared variable modification, for another thread is not immediately visible.

Classdata{inta =0;intb =0;intx =0;inty =0;//A thread execution Publicvoidthreada () {a = 1;    x = b;        }//b Thread Execution publicvoidthreadb () {b = 2;    y = A; }}

If there are two threads executing the Threada and threadb methods separately. There may be x==y==0 this situation (of course, the situation is relatively rare).

Since A and B are assigned values and have not yet been flushed into main memory, execute x = b and y = a statements, and this time the thread does not know that A and B have also been modified, still the original value of 0.

1.3 Ordering

To improve program execution performance, the Java memory model allows the compiler and processor to reorder instructions. The reordering process does not affect the execution of a single-threaded procedure, but it can affect the correctness of multithreaded concurrency execution.

Classreorder{intx =0;booleanflag =false;publicvoidwriter () {x = 1;    Flag =true;            }publicvoidreader () {if (flag) {INTA = x * x;    ...        } }}

For example, in the previous example, we used the flag variable, and the flag x variable has been assigned a value. However, there is no data dependency between the two statements, so they may be reordered, that is, the flag = True statement will precede the X = 1 statement, so will this change cause problems?

In single-threaded mode, there is no problem, because the writer method is a whole, only when the writer method executes, the other methods can execute, so the flag = True statement and the X = 1 statement order changes have no effect.

In multithreaded mode, it may cause problems, because the writer method is not finished, the reader method is called by another thread, this time if the flag = True statement and the X = 1 statement order changes, it is possible to produce flag is true, but X has not been assigned a condition, Unlike the program's intention, it creates unexpected problems.

1.4 Atomicity

In Java, read and assign operations to variables of the base data type are atomic operations, meaning that these operations are not interrupted, executed, or not executed.

x =1;//atomicity y = x;//not atomic x = x +1;//not atomic x++;//not atomic System.out.println (x);//Atomicity

Equation 2: There are two atomic operations, read the value of x, assign to Y. Equation 3: Also three atomic operations, read the value of x, plus 1, assign to X. Equation 4: Same as Equation 3.

So there are two types of atomic operations: 1. Assigns a basic data type constant to a variable. 2. Read the variable value of the base data type. Any calculation operation is not atomic.

1.5 Summary

Multithreaded operations share variables, resulting in the above three issues, visibility, ordering, and atomicity.

Visibility: A thread changes the shared variable and may not immediately flush to the main memory, when another thread reads the shared variable, which is the value before the change. So the change to this shared variable is not visible to other threads.

Ordering: The compiler and the processor reorder the instructions, and the order of the statements changes, so that in the case of multithreading, strange exceptions can occur.

Atomicity: Only read and assign operations to variables of the basic data type are atomic operations.

There are two ways to solve these three problems:

Volatile keyword: it can only solve two problem visibility and ordering problems, but if volatile modifies the base data type variable, and this variable only does read and assign operations, then there is no atomic problem. For example, use it to modify a Boolean variable.

Locking: You can guarantee that only the same thread operates shared variables at the same time, and that shared variables are not modified by other threads when the current thread operates on shared variables, so visibility, ordering, and atomicity issues are resolved. The lock lock is divided into synchronized synchronous lock and Juc frame.

Two. volatile keyword

volatile keyword action

1. Visibility: Reading a volatile variable, you can always see (any thread) the last write to this volatile variable.

Ordering: Prohibit order reordering, that is, in the program in the volatile variable operation, before its operation must have been fully executed, and the result is already visible to the subsequent operation, after which the operation must not be executed.

This specific explanation, please see "in-depth understanding of the Java memory Model" in the Happens-before rules of the explanation.

CLASSVOLATILEFEATURESEXAMPLE{//uses volatile to declare a basic data type variable VLVOLATILELONGVL =0l;//for a single volatile basic data type variable assignment publicvoidset (LONGL)    {VL = L;    }//compound operation for a single volatile basic data type variable publicvoidgetandincrement () {vl++;    }//read Publiclongget () {RETURNVL for a single volatile basic data type variable;    }}classvolatilefeaturesexample{//declares a basic data type variable VLLONGVL =0l;//equivalent to adding a synchronous lock Publicsynchronizedvoidset (LONGL) {VL = L;        }//Common Method Publicvoidgetandincrement () {longtemp = get ();        Temp +=1l;    Set (temp);    }//equivalent to adding a synchronous lock Publicsynchronizedlongget () {RETURNVL; }}

If volatile modifies the base data type variable and only reads and assigns the variable, it is equivalent to adding a synchronous lock.

Three. Synchronized Sync lock

Synchronized a synchronous lock is an access to a locked resource, the other thread must block the wait as long as the thread that acquires the lock can manipulate the locked resource.

So a thread says that it can block wait and run, so what are the threads going to be?

3.1 Thread Status


State transition Diagram

Threads are divided into 5 states:

New State (new): Creates a thread object, then the thread object is the new state.

Operational status (Runnable): Indicates that the thread thread is ready to run at any time, as long as the CPU is getting execution rights.

Note: This state can be converted by a new state (by invoking the Start method of the thread) or by a blocking state.

Running state (Running): Indicates that the thread is running and that the running state can only be reached from the operational state.

Blocking state (Blocked): Indicates that the thread is currently stopped and is divided into three main cases:

1). Synchronization blocking state: The thread acquires a synchronization lock failure and enters a synchronous blocking state.

2). Wait for blocking state: The thread calls the Wait method to enter the state. Note: The nature of the Join method is also implemented by the wait method.

3). Other blocking states: Let the thread sleep through the Thread.Sleep method, turn on the IO stream and let the thread wait for blocking.

Dead State (Dead): When the thread's Run method is complete, the threads go into a dead state. The state can no longer be converted to another state.

3.2 Synchronized synchronous method or synchronization block

How does the synchronized synchronization method or synchronization block work?

It is equivalent to having a large room with a lock on the door, and all the synchronization methods or synchronization blocks associated with the lock lock are stored in the room.

When a thread is going to execute a synchronous method or synchronous block of the lock lock, it comes to the door of the room, and if the lock lock is found, then it enters the room with a lock and locks the room, which can execute any synchronization method or synchronization block in the room.

Then there is another thread to execute this lock lock of a synchronous method or synchronization block, it came to the door of the room, found that the lock is not, can only wait outside the door, at this time the thread in the synchronized synchronous blocking thread pool.

When the thread that gets the lock locks, the synchronization method or the synchronization block code executes, it exits from the room and puts the lock on the door.

The thread waiting outside the door grabs the lock, the thread that gets the lock can enter the room, and the other threads continue to wait.

Note: The synchronized lock is the synchronization method or synchronization block that locks all associated with this lock.

What exactly is synchronized's sync lock?

is actually a Java object, in Java, each object has a lock tag (monitor), also known as a monitor, when multiple threads access an object at the same time, the thread only obtains the lock of the object to access.

3.3 Wait with notify, Notifyall

These three methods are mainly used to implement the problem of waiting between threads.

The wait method that invokes the object lock causes the current thread to wait for the thread that is putting the current thread in the object lock to wait for the pool. The Notify method that invokes the object lock wakes a thread randomly from the thread waiting pool, and the Notifyall method wakes all threads.

Note: The wait for object lock and the Notify, Notifyall method call must be placed in a synchronous method or synchronous block with the lock of the object lock, or the illegalmonitorstateexception exception will be thrown.

Wait and notify, Notifyall specifically how to operate it?

The previous procedure, like the one described in synchronized, when the wait method that locks lock is called, the thread (that is, the current thread) exits the room, returns the lock lock, but not into the synchronized synchronization blocking thread pool, but into the lock lock thread waiting for the pool.

Then another thread gets the lock lock to the room, and if it executes the lock lock's Notify method, it will randomly wake a thread from the lock lock thread waiting for the pool and put it into the synchronized synchronous blocking line Cheng ( Remember that only the thread that gets the lock locks the room). Invokes the Notifyall method of lock lock, that is, the wake thread waits for all the threads of the pool.

Note: When a wait-blocked thread enters the synchronized synchronization code block again, execution continues from the time after the wait method call.

The thread in the lock lock waits for a thread in the pool, only four ways to wake up:

Wake-up via Notify ()

Wake-up via Notifyall ()

Wake-up via interrupt () interrupt

If you enter a waiting state by calling wait (long timeout), it will also wake up when the time expires.

Note The wait, notify, and Notifyall methods must acquire the lock before they can be called, or the illegalmonitorstateexception exception is thrown. Only the synchronized module can get the lock for the current thread, so the wait method can only be executed in the Synchronized module.

Four. Other important methods

4.1 Join method

Allows the current thread to wait for another thread to finish executing before continuing.

Publicfinalvoidjoin () throwsinterruptedexception {join (0); }publicfinalsynchronizedvoidjoin (Longmillis) throwsinterruptedexception {//Gets the current system number of milliseconds Longbase = System.currenttimemillis (); Longnow =0;//Millis less than 0, throws an exception if (Millis <0) {thrownewillegalargumentexception ("timeout        Value is negative ");            }if (Millis ==0) {//isAlive determines whether the current thread is alive while (IsAlive ()) {//Wait (0) indicates that the current thread waits indefinitely wait (0);                }}else{//determines whether the current thread survives while (isAlive ()) {Longdelay = millis-now;if (delay <=0) {break, through isAlive).                }//the current thread waits for a delay of milliseconds, and the current thread is awakened wait (delay) over time;            now = System.currenttimemillis ()-base; }        }    }

The Join method is a method in thread, and the lock object that the Synchronized method synchronizes is the thread object, which lets the current thread wait by invoking the wait method of the Thread object

Note: This is where the current thread waits, that is, the thread that is currently calling the Join method, not the thread object. So when is the front-line going to wake up?

When the thread object threads execute and enter the dead state, the thread object's Notifyall method is called to wake up the threads object to wait for all threads in the pool.

Example:

Publicstaticvoidjointest () {        thread thread =newthread (newrunnable () {      & nbsp     @Overridepublicvoidrun () {for (inti =0; I <10; i++) {try{              and nbsp         Thread.Sleep (+);                   }catch (Interruptedexception e) {                        e.printstacktrace ();    &NB Sp              }                    Sy Stem.out.println (Thread.CurrentThread (). GetName () + ":  i===" +i);              & nbsp }           }       }, "T1");        Thread.Start () ;try{            Thread.Join ();       }catch (InterruptedeXception e) {            e.printstacktrace ();       }    &NBS P   System.out.println (Thread.CurrentThread (). GetName () + ": End");   }

4.2 Sleep method

Just let the current thread wait for a certain amount of time before continuing execution.

4.3 Yield method

Moves the current thread state from the running state to the operational state, and resumes execution if the CPU execution is acquired again.

4.4 Interrupt Method

Interrupt thread, which interrupts threads that are in a blocking state, but does not play any role in the running state of the thread.

Example:

Publicstaticvoidinterrupttest () {//Threads thread thread =newthread (newrunnable () {        in a blocked state     @Overridepublicvoidrun () {try{                    System.ou T.println (Thread.CurrentThread (). GetName () + "start");                    T Hread.sleep (+);                    SYSTEM.OUT.PRINTLN ( Thread.CurrentThread (). GetName () + "End");               }catch ( Interruptedexception e) {                    SYSTEM.OUT.PRINTLN (THREAD.C Urrentthread (). GetName () + "Generate exception");               }        &NB Sp  }       }, "T1");        Thread.Start ();//threads in the running state thread Thread1 =NEWTHR EAD (newrunnable () {            @OvErridepublicvoidrun () {                SYSTEM.OUT.PRINTLN (Thread.CurrentThread ( ). GetName () + "Start"), Inti =0;while (i < integer.max_value-10) {                &N Bsp   i = i +1;for (intj =0; J < I; j + +);               }                SYSTEM.OUT.PRINTLN (Thread.CurrentThread (). GetName () + "i==" +i);      & nbsp         SYSTEM.OUT.PRINTLN (Thread.CurrentThread () getName () + "End");           }       }, "T2");        Thread1.start ();try{      &N Bsp     Thread.Sleep;       }catch (interruptedexception e) {        &NB Sp   E.printstacktrace ();       }        SYSTEM.OUT.PRINTLN ( Thread.CurrentThread (). GetName () + "forInterrupt ");        Thread.Interrupt ();        thread1.interrupt ();   }

4.5 Isinterrupted Method

Returns whether this thread has been interrupted. Note When the thread's interrupt method is called, the method of the thread's isinterrupted returns TRUE. If the exception is handled, the flag position bit is false, which means that the Isinterrupted method returns false.

4.6 Thread priority and daemon thread

In Java, the thread priority range is 1~10, and the default priority is 5.

The thread in Java is divided into user threads and daemon threads, and Isdaemon returns true to indicate that it is a daemon thread. When all the user threads have finished executing, the Java virtual machine exits, regardless of whether or not the daemon thread has finished executing.

When a new thread is created, the priority of the new thread is equal to the priority of the thread that created it, and the new thread is the daemon thread only when it is created as a daemon thread.

Of course, you can also modify the priority of the thread through the SetPriority method, and the Setdaemon method sets whether the thread is a daemon thread.

Five. Example explanation

5.1 Without any sync lock

Importjava.util.collections;importjava.util.list;importjava.util.concurrent.copyonwritearraylist; Importjava.util.concurrent.countdownlatch;classdata {intnum;publicdata (intnum) {this.num = num;   } Publicintgetanddecrement () {returnnum--;   }}classmyrun implements Runnable {Privatedata data;// The number Privatelistlist;privatecountdownlatch latch;publicmyrun (data data, listlist, Countdownlatch latch) used to record all sell tickets { This.data = Data;this.list=list;this.latch = latch;   }    @Overridepublicvoidrun () {try{  & nbsp         action ();       }  finally {//release latch shared lock Latch.countdown ();  &N Bsp    }   }//for ticket operation, note that data.num>0 is not used as a condition of judgment until the thread exits. Doing so will result in the use of shared variable data.num in both places, so more needs to be considered when doing multi-threaded synchronization. This is only for loops 5 times, which means that each thread only sells 5 tickets and stores all the sell numbers in the list collection. Publicvoidaction () {for (inti =0; I <5; i++) {try{                Thread.Sleep (10 );        &NBSP  }catch (interruptedexception e) {                E.printstacktrace ();           }intnewnum = data.getanddecrement ();            SYSTEM.O UT.PRINTLN ("Thread" +thread.currentthread (). GetName () + "  num==" +newnum) List.add (newnum);       }   }}publicclassthreadtest {publicstaticvoidstartthread (data data, String name, Listlist, Countdownlatch latch) {        Thread T =newthread (Newmyrun (data,list, latch), name);    &NB Sp   T.start ();   }publicstaticvoidmain (string[] args) {//Use Countdownlatch to let the main thread wait for the child thread to finish executing, Before the end Countdownlatch latch =newcountdownlatch (6); Longstart = System.currenttimemillis ();//here with Concurrent List collection listlist= Newcopyonwritearraylist ();        Data Data =newdata (+);        Startthread ( Data, "T1", list, latch);        startthread (data, "T2", List, latCH);        startthread (data, "T3", list, latch);        startthread (data, "T4", list, latch);        startthread (data, "T5", list, latch);        Startthread (data , "T6", list, latch);try{            latch.await ();       }catch (interru Ptedexception e) {            e.printstacktrace ();       }//process the list collection , sort and flip collections.sort (list);        collections.reverse (list);        SYSTEM.OUT.PRINTLN (list); longtime = System.currenttimemillis ()-start;//output altogether takes time System.out.println ("\ nthe main thread end time= = "+time);   }}

The result of the output is

Thread t2num==29 thread t6num==27 thread t5num==28 thread t4num==28 thread t1num==30 thread t3num==30 thread t2num==26 thread t4num==24 thread t6num==25 thread t5num== 23 Thread t1num==22 thread t3num==21 thread t4num==20 thread t6num==19 thread t5num==18 thread t2num==17 thread t1num==16 thread t3num==15 thread t4num==14 thread t5num== 12 thread t6num==13 thread t1num==9 thread t3num==10 thread t2num==11 thread t1num==8 thread t6num==5 thread t2num==7 thread t5num==3 thread t3num==4 thread t4num==6[ 30,30,29,28,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3] Main thread End time==62

From the results found the problem, there is a duplicate vote, so 30 tickets are not sold out. The main reason is that the Getanddecrement method operation of the data class is not multithreaded security.

First it cannot guarantee atomicity, divided into three operations, first reading the value of NUM, then num self-subtraction, before returning the value of the decrement.

Because NUM is not a volatile keyword modifier, it does not guarantee visibility and ordering.

So as long as the Getanddecrement method is more thread-safe, then you can solve the problem above. So how to ensure that the Getanddecrement method is multi-thread safe? The simplest way is to add the Synchronized keyword before the Getanddecrement method.

This is the synchronized key lock is the data object instance, so when the Getanddecrement method is guaranteed to be called by multiple threads, only one thread can call and wait for the call to complete before other threads can invoke the Getanddecrement method.

Because only one thread calls the Getanddecrement method at a time, it does not worry about changing the num variable when it does the num--operation. So atomicity, visibility and ordering can be guaranteed.

5.2 Using the minimum sync lock

Classdata{intnum;    Public Data (intnum) {this.num=num;    }//adds the Getanddecrement method to the Sync Lock public synchronizedintgetanddecrement () {returnnum--; }}

Output results

Thread t1num==30 thread t2num==29 thread t6num==28 thread t4num==26 thread t3num==27 thread t5num==25 thread t6num==22 thread t2num==21 thread t3num==23 thread t1num== 24 thread t4num==20 thread t5num==19 thread t2num==18 thread t3num==17 thread t5num==13 thread t4num==14 thread t6num==16 thread t1num==15 thread t2num==12 thread t4num== 9 thread t1num==7 thread t5num==10 thread t3num==11 thread t6num==8 thread t4num==6 thread t2num==3 thread t1num==2 thread t3num==4 thread t5num==5 thread t6num==1[ 30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1] Main thread End time==61

We just added the Getanddecrement method of data to the synchronous lock and found that the multithreading concurrency problem was solved. The main reason is that we only use the shared variable num in one place, so we just need to synchronize this. And you'll find that the total amount of time spent in the end is almost the same as when the sync lock is not synchronized, because our sync code is small enough.

On the contrary, we add the synchronization lock is unreasonable, may also be able to achieve multithreading security, but time will be greatly increased.

5.3 Unreasonable use of sync lock

@Overridepublicvoidrun () {try{synchronized (data) {action ();        }}finally{//release latch shared lock Latch.countdown (); }    }

Input Result:

Thread t1num==30 thread t1num==29 thread t1num==28 thread t1num==27 thread t1num==26 thread t6num==25 thread t6num==24 thread t6num==23 thread t6num==22 thread t6num== 21 Thread t5num==20 thread t5num==19 thread t5num==18 thread t5num==17 thread t5num==16 thread t4num==15 thread t4num==14 thread t4num==13 thread t4num==12 thread t4num== 11 thread t3num==10 thread t3num==9 thread t3num==8 thread t3num==7 thread t3num==6 thread t2num==5 thread t2num==4 thread t2num==3 thread t2num==2 thread t2num==1[ 30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1] Main thread End time==342

Here we will put the entire action method into the synchronous code block, but also can solve the multi-threaded conflict problem, but the time spent is on the Getanddecrement method and the synchronization lock time several times.

So when we're adding a sync lock, those need to be synchronized to see where shared variables are used. For example, there are only synchronous variables used in the Getanddecrement method, so just lock it up.

However, if you use data.num>0 as the loop condition in the action method, the entire action method must be placed in the synchronization module when the sync lock is added, because we must ensure that the data.num> 0 The Getanddecrement method calls the code is in the synchronization module, otherwise there will be a multi-threaded conflict problem.

Welfare:

Want to know more multi-threaded knowledge points, you can pay attention to me, I will also organize more on the multi-threaded this piece of knowledge to share out, in addition to recommend an Exchange Learning Group: 650385180, which will share some senior architect recorded video recording: Spring,mybatis , Netty source analysis, high concurrency, performance, distributed, multi-threading, micro-service architecture principles, JVM Performance optimization These become the necessary knowledge system architects. You can also receive free learning resources and benefit from this at the moment.


Dry: Java Multithreading details (enclosed source code)

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.