Synchronized is one of the most common methods for solving concurrency problems in Java, and is the simplest one. There are three main functions of synchronized: (1) ensure thread-mutually exclusive access synchronization code (2) Ensure that changes to shared variables are visible in time (3) to resolve reordering issues effectively.
Each object in Java can be used as a lock, which is the basis for synchronized to implement synchronization:
1, the normal synchronization method, the lock is the current instance object
public class Synchronizedtest {4 public synchronized void Method1 () {5 System.out.println ("Method 1 start"); 6 try {7 System.out.println ("Method 1 Execute"), 8 thread.sleep (+); 9} catch (Interruptedexception e) {e.printstacktrace ();}12 System.out.println ("Method 1 End");}14 public syn chronized void Method2 () {System.out.println ("Method 2 Start"), + try {System.out.println ("Method 2 Execute"); Thread.Sleep (+); catch (Interruptedexception e) {E.print StackTrace ();}23 System.out.println ("Method 2 End");}25. public static void Main (string[] args) {final synchronizedtest test = new Synchronizedtest (); New Thread (New Runnable () {30 @Override31 public void Run () {test.method1 ();}34}). Start (); 35-New Thread (new Runnable () {PNS @Override38 public void Run () {test. Method2 ();}41}). Start (); 42}43}
2, static synchronization method, lock is the class object of the current classes
public class Synchronizedtest {4 public static synchronized void Method1 () {5 System.out.println ("Method 1 Start "); 6 try {7 System.out.println ("Method 1 Execute"); 8 Thread.Sleep; 9} CA TCH (interruptedexception e) {e.printstacktrace ();}12 System.out.println ("Method 1 en D ");}14 public static synchronized void Method2 () {System.out.println (" Method 2 Start "); 17 try {System.out.println ("Method 2 Execute"), Thread.Sleep (+), The catch ( Interruptedexception e) {e.printstacktrace ();}23 System.out.println ("Method 2 End"); 2 4}25 public static void Main (string[] args) {final synchronizedtest test = new Synchronizedtest (); final synchronizedtest test2 = new Synchronizedtest (); New Thread (New Runnable () {31 @Overridepublic void Run () {test.method1 ();}35}). Start (); 36 37 New Thread (New Runnable () {@Override39 public void run () {Test2.metho D2 ();}42}). Start (); 43}44}
3, synchronous method block, lock is the object inside the parentheses
public class Synchronizedtest {4 public void method1 () {5 System.out.println ("Method 1 Start"); 6 try {7 synchronized (this) {8 System.out.println ("Method 1 Execute"); 9 Thread. Sleep (}11} catch (Interruptedexception e) {e.printstacktrace (); 13}14 System.out.println ("Method 1 End");}16 public void Method2 () {System.out.println ("method 2 start "), try {synchronized (this) {System.out.println (" Method 2 Execute "); 22 Thread.Sleep (+);}24} catch (Interruptedexception e) {E.printsta Cktrace ();}27 System.out.println ("Method 2 End");}29 public static void Main (string[] Arg s) {final synchronizedtest test = new Synchronizedtest (); New Thread (New Runnable () {34 @Override35 public void Run () {test.method1 (); PNs}38}). Start (); New Thread (New Runnable () {@Override42 public void run () {test.method2 (); 44}45 }). Start (); 46}47}
Synchronize underlying principle:
Synchronization in a Java virtual machine (synchronization) is based on entering and exiting the monitor object implementation, whether explicit synchronization (with explicit Monitorenter and monitorexit directives, that is, synchronous code blocks) or implicit synchronization. In the Java language, the most common place to synchronize is possibly a synchronized-modified synchronization method. The synchronous method is not implemented synchronously by the Monitorenter and monitorexit directives, but is implicitly implemented by the method invocation instruction reading the acc_synchronized flag of the method table structure in the run-time pool, which is discussed in detail later.
Synchronous code block: The monitorenter instruction is inserted at the beginning of the synchronization code block, and the monitorexit instruction is inserted at the end of the synchronization code block, and the JVM needs to ensure that each monitorenter has a monitorexit corresponding to it. Any object has a monitor associated with it, and when a monitor is held, he will be locked. When the thread executes to the monitorenter instruction, it attempts to acquire the monitor ownership of the object, that is, to attempt to acquire the object's lock;
In the JVM, the layout of objects in memory is divided into three areas: object headers, instance variables, and fill data. As follows:
Instance variable: holds the property data information for the class, including the property information of the parent class, and if the instance part of the array also includes the length of the array, this portion of memory is aligned 4 bytes.
Populate data: Because the virtual machine requires that the object start address must be an integer multiple of 8 bytes. Populating the data is not a must, just for byte alignment, this is understood.
Object Header: The object header of a hotspot virtual machine mainly consists of two parts: Mark Word (marker field), Klass Pointer (type pointer). Where Klass point is a pointer to the class metadata that the object is pointing to, the virtual machine uses this pointer to determine which class instance this object is, and Mark word is used to store the runtime data of the object itself, which is the key to implementing lightweight and biased locks.
Mark Word: The runtime data used to store the object itself, such as hash code (HASHCODE), GC generational age, lock status flag, thread-held lock, biased thread ID, biased timestamp, and so on. Java object header generally occupies two machine code (in 32-bit virtual machine, 1 machine code equals 4 bytes, that is, 32bit), but if the object is an array type, then three machine code is required because the JVM virtual machine can determine the size of Java object through the metadata information of Java object. However, the size of the array cannot be confirmed from the metadata of the array, so a piece is used to record the array length.
Monior: We can interpret it as a synchronization tool or as a synchronization mechanism, which is usually described as an object. Like everything else, all Java objects are inherently monitor, and every Java object has the potential to be a monitor, because in Java design, every Java object comes out of womb with an invisible lock, called an internal lock or monitor lock. Monitor is a thread-private data structure, with each thread having a list of available monitor record, along with a global list of available. Each locked object is associated with a monitor (the Lockword in the Markword of the object header points to the start address of the monitor), while the monitor has an owner field that holds the unique identity of the thread that owns the lock, indicating that the lock is occupied by the thread. The structure is as follows:
Owner: Initial null indicates that no thread currently owns the monitor record, that the thread is uniquely identified when the thread has successfully owned the lock, and is set to NULL when the lock is disposed;
ENTRYQ: Associates a system mutex (semaphore) that blocks all threads that attempt to lock the monitor record failed.
Rcthis: Represents the number of all threads on the monitor record that are blocked or waiting.
Nest: Used to implement a count of reentrant locks.
Hashcode: Saves the hashcode value copied from the object header (may also contain GC age).
Candidate: Used to avoid unnecessary blocking or waiting for a thread to wake up, because each time only one thread succeeds in owning a lock, if each previous thread that released the lock wakes all the threads that are blocking or waiting, Causes an unnecessary context switch (from blocking to ready and then being blocked because of a competing lock failure), resulting in a severe performance degradation. Candidate there are only two possible values of 0 to indicate that no wake-up thread 1 is required to wake a succession thread to compete for the lock.
Java Virtual machine optimization for synchronize:
There are four types of locks, lock-free, biased, lightweight and heavy-lock. With the lock competition, the lock can be upgraded from a biased lock to a lightweight lock, and then upgrade the weight of the lock, but the lock upgrade is one-way, that is, only from low to high upgrade, no lock degradation, about the heavy lock, we have analyzed in detail before, below we will introduce the bias lock and lightweight lock and other optimizations of the JVM
Biased lock
The biased lock is a new lock added after Java 6, it is an optimization method for the lock operation, after study found that in most cases, the lock is not only a multi-threaded competition, and always by the same thread multiple times, so in order to reduce the same thread acquisition lock (will involve some CAS operations, time-consuming) The price of the bias lock is introduced. The core idea of biased locking is that if a thread acquires a lock, then the lock enters the bias mode, and the structure of Mark Word becomes a biased lock structure, and when the thread requests the lock again, there is no need to do any more synchronization, that is, the process of acquiring the lock, which saves a lot of operation on the lock request. The performance of the program is thus provided. Therefore, for the absence of the lock competition, the bias lock has a good optimization effect, after all, it is very likely that the same thread multiple times the same lock request. But for the lock competition more intense occasions, the bias lock is invalid, because such occasions are very likely each application lock thread is not the same, so this situation should not use biased lock, otherwise it will outweigh the loss, it should be noted that the bias lock failure, and will not immediately expand into a heavy lock, but first upgrade to a lightweight lock.
Lightweight lock
If the lock fails, the virtual machine does not immediately upgrade to a heavyweight lock, it also attempts to use an optimization method called lightweight locking (added after 1.6), at which point the structure of Mark Word becomes a lightweight lock structure. Lightweight locks can enhance the performance of the program is based on the "most of the lock, the entire synchronization period of no competition", note that this is empirical data. It is necessary to understand that the light-weight lock adapts to the scenario where the thread alternately executes the synchronization block, and if there is an occasion to access the same lock at the same time, it causes the lightweight lock to expand to a heavyweight lock.
Spin lock
After a lightweight lock fails, the virtual machine does an optimization called spin lock in order to prevent the thread from actually suspending at the operating system level. This is based on the majority of cases where the thread holds the lock for not too long, and if the thread that hangs the operating system level directly may outweigh the gains, after all, the operating system switches between threads that need to transition from the user state to the kernel mentality, the transition between the states takes relatively long time, the time cost is relatively high, So the spin lock assumes that in the near future, the current thread can acquire the lock, so the virtual opportunity makes a few empty loops for the thread that is currently trying to acquire the lock (this is also known as spin reasons), usually not too long, possibly 50 loops or 100 loops, and, after several cycles, if the lock is obtained, it enters the critical section. If the lock is not yet available, it will hang the thread at the operating system level, which is how the spin lock is optimized, and this is actually a way to improve efficiency. Finally, there is no way to upgrade to a heavyweight lock.
Lock removal
The elimination lock is another kind of lock optimization of the virtual machine, this kind of optimization is more thorough, when the Java Virtual machine is JIT compiled (can be easily understood as when a piece of code will be executed for the first time to compile, also known as the instant compile), through the scanning of the running context, remove the lock cannot exist the competition of the shared resources, By eliminating unnecessary locks in this way, you can save meaningless request lock times, as follows StringBuffer append is a synchronous method, but the StringBuffer in the Add method belongs to a local variable and is not used by other threads. As a result, StringBuffer cannot have a shared resource race scenario, and the JVM automatically removes its lock.
/** * Created by Zejian on 2017/6/4. * Blog:http://blog.csdn.net/javazejian * Eliminate StringBuffer sync lock */public class Stringbufferremovesync { public void Add (String str1, String str2) { //stringbuffer is thread safe because SB is only used in Append methods and cannot be referenced by other threads //So that SB belongs to a resource that cannot be shared, The JVM automatically eliminates the internal lock stringbuffer SB = new StringBuffer (); Sb.append (STR1). Append (str2); } public static void Main (string[] args) { Stringbufferremovesync rmsync = new Stringbufferremovesync (); for (int i = 0; i < 10000000; i++) { rmsync.add ("abc", "123");}}}
Synchronize re-entry:
From a mutex design, when a thread attempts to manipulate a critical resource held by another thread for an object lock, it will be in a blocking state, but when a thread requests its own critical resource to hold the object lock again, this is a re-entry lock, and the request succeeds. In Java, synchronized is an atomic-based internal locking mechanism that is reentrant, so that another synchronized method is called within its method body while one thread calls the Synchronized method, This means that a thread gets an object lock and then requests the object lock again, which is allowed, which is the reentrant nature of the synchronized. As follows:
public class Accountingsync implements runnable{ static Accountingsync instance=new accountingsync (); static int i=0; static int j=0; @Override public Void Run () { for (int j=0;j<1000000;j++) { //this, current instance object lock synchronized (this) { i++; Increase ();//synchronized's Reentrant }}} public synchronized void Increase () { j + +; } public static void Main (string[] args) throws interruptedexception { thread t1=new thread (instance); Thread T2=new thread (instance); T1.start (); T2.start (); T1.join (); T2.join (); System.out.println (i); }}
As the code demonstrates, after acquiring the current instance object lock and entering the synchronized code block to execute the synchronous code, and calling the other synchronized method of the current instance object in the code block, the current instance lock is requested again, and then the method body code is executed. This is the most direct embodiment of the re-entry lock, you need to pay special attention to the other case, when the subclass inherits the parent class, the subclass is also able to call the parent class through a reentrant lock synchronization method. Note that because the synchronized is based on monitor, the counters in monitor will still add 1 each time you re-enter.
Thread break: Just like the meaning of the interrupt word, interrupt it in the middle of the thread run (the Run method), in Java, provide the following 3 methods for threading interrupts
The thread break (instance method) public void Thread.Interrupt ();//Determines whether the threads are interrupted (instance method) public boolean thread.isinterrupted ();// Determine if it is interrupted and clear the current interrupt state (static method) public static Boolean thread.interrupted ();
Wait for wake mechanism and synchronize: the so-called wake-up mechanism this article mainly refers to the notify/notifyall and wait methods, when using these 3 methods, must be in the synchronized code block or synchronized method, Otherwise, the illegalmonitorstateexception exception is thrown because the monitor object of the current object must be obtained before invoking these methods, that is, the notify/notifyall and wait methods depend on the monitor object , in the previous analysis, we know that monitor exists in Mark Word (which stores the monitor reference pointer) in the object header, and the Synchronized keyword can get monitor, which is why notify/ The Notifyall and wait methods must be in the synchronized code block or the reason that the synchronized method is called.
This article references: Http://blog.csdn.net/javazejian/article/details/72828483?locationNum=5&fps=1
Http://www.cnblogs.com/pureEve/p/6421273.html
Http://www.cnblogs.com/paddix/p/5367116.html
Java concurrent programming synchronized and its implementation principle