Java Memory Model
Thread synchronization
Thread synchronization mechanism is a set of data access mechanism suitable for coordinating threads, which can ensure thread safety
The thread synchronization mechanism provided by the java platform includes lock, volatile keyword, final keyword, static keyword, and related API s such as object.wait/object.notify
Lock overview
The premise of thread safety problem is that multiple threads access shared data concurrently. The concurrent access of multiple data to shared data is transformed into serial access, that is, shared data can only be accessed by one thread. Lock is this idea.
When a thread accesses data, it must first obtain a lock. The thread that obtains the lock is called the lock holding thread. A lock can only be held by one thread at a time. The code executed by the holding thread after obtaining the lock and before releasing the lock is called the critical area.
Locks are exclusive, that is, a lock can only be held by one thread. This kind of lock is called exclusive lock or mutex lock.
In the JVM part, locks are divided into internal locks and display locks. Internal locks are implemented through the Synchronized keyword, and display locks are implemented through the Synchronized keyword
Implemented by the java.concurrent.locks.Lock interface implementation class.
Function of lock
Lock can realize the security of shared data and ensure the atomicity, visibility and order of threads.
Locks guarantee atomicity through mutual exclusion. A lock can only be held by one thread, which ensures that the code in the critical area can only be executed by one thread at a time, so that the operations performed by the code in the critical area naturally have the characteristics of indivisibility and atomicity.
For example, all vehicles in a road section are running and executing concurrently. When passing through a road section, multiple lanes become one lane. Only one vehicle can pass through at a time, and the concurrent execution is changed to serial execution.
Visibility refers to the two actions of flushing the processor cache by the write thread and refreshing the processor cache by the read thread. The acquisition of the lock implies the action of refreshing the processor cache, and the release of the lock implies the action of flushing the processor cache.
The lock can guarantee the order. The critical area executed by the write thread in the critical area looks like it is executed completely in the order of the source code.
Related concepts of lock
Reentrant: when a thread holds the lock, it can apply for the lock again / multiple times
If a thread holds a lock and has not released it, but can continue to successfully apply for the lock, it is said that the lock can be re entered, and vice versa.
Lock contention and scheduling
Internal locks in java belong to unfair locks, and display locks support unfair locks and fair locks
Lock granularity
The number and size of shared data that can be protected is called lock granularity.
Lock protection shares a large amount of data, which is called coarse lock granularity, otherwise it is called fine granularity.
Too coarse lock granularity will cause threads to wait unnecessarily when applying for locks. Too fine lock granularity will increase the overhead of lock scheduling.
For example, if a bank has a counter where an employee can open a card, close an account, withdraw cash and loan, then everyone can only go to this counter to handle business, which will take a long waiting time. However, if the business is subdivided into one business and one counter, it will increase the bank's expenses and require three employees.
Internal lock: Synchronized
Every object in Java has an internal lock associated with it. This lock is also called monitor. It is an exclusive lock, which can ensure atomicity, visibility and exclusivity.
Synchronized(Object lock) { Sync code block, where you can access shared data }
The modified instance method is called synchronous instance method, and the modified static method is called synchronous static method.
Synchronized synchronization code block
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); for (int i = 0; i <2 ; i++) { new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm(); } }.start(); } } public void mm() { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
The code of both threads is executing concurrently
Now synchronize when printing. The principle of synchronization is that the thread must first obtain the lock when executing
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); for (int i = 0; i <2 ; i++) { new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm();//The object using the lock is the synchronizedLock object } }.start(); } } public void mm() { synchronized (this)//this as the current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
Because the Synchronized internal lock is an exclusive lock, it can only be held by one thread at a time. Now Thread-0 obtains the lock object first. Thread-1 waits for Thread-0 to release the lock after execution in the waiting area, and thread-1 obtains the lock before execution.
Different lock objects cannot be synchronized
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); SynchronizedLock synchronizedLock2=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm();//The object using the lock is the synchronizedLock object } }.start(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock2.mm();//The object using the lock is the synchronizedLock object } }.start(); } public void mm() { synchronized (this)//this as the current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
Therefore, you must use the same lock object to synchronize
Use constants as lock objects
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); SynchronizedLock synchronizedLock2=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm();//The object using the lock is the synchronizedLock object } }.start(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm();//The object using the lock is the synchronizedLock object } }.start(); } public static final Object obj=new Object(); public void mm() { synchronized (obj)//Constant as current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
Synchronous instance method
Use synchronized to modify the instance method and synchronize the instance method. this is used as the lock object by default
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm(); } }.start(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm2(); } }.start(); } //Synchronous instance method public synchronized void mm() { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } public void mm2() { synchronized (this)//Constant as current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
Synchronous static method
Use synchronized to modify static methods. Synchronize static methods. By default, SynchronizedLock class is used as the lock object at runtime
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm2(); } }.start(); new Thread(new RunnableThread()) { @Override public void run() { SynchronizedLock.mm();//The object using the lock is SynchronizedLock.class } }.start(); } //Synchronous static method public synchronized static void mm() { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } public void mm2() { synchronized (SynchronizedLock.class)//Constant as current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
How to select synchronization code blocks and synchronization methods
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { try { synchronizedLock.mm2(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread(new RunnableThread()) { @Override public void run() { try { synchronizedLock.mm2();//The object using the lock is SynchronizedLock.class } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } //The granularity of synchronous instance method lock is coarse, and the execution efficiency is low public synchronized void mm() throws InterruptedException { long starttime= System.currentTimeMillis(); System.out.println("start"); Thread.sleep(3000); for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } System.out.println("end"); long Endtime= System.currentTimeMillis(); System.out.println(Endtime-starttime); } //Synchronous code block lock has fine granularity and high concurrency efficiency public void mm2() throws InterruptedException { System.out.println("start"); Thread.sleep(3000); synchronized (this)//Constant as current object { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } System.out.println("end"); } }
When executing the synchronization method, the two thread calls need to sleep for three seconds each time, while the synchronization code block starts the thread at the same time for three seconds, which is more efficient
Dirty reading
public class Test06 { public static void main(String[] args) throws InterruptedException { User user=new User(); SubThread subThread=new SubThread(user); subThread.start(); user.GetName(); } static class SubThread extends Thread { public User user; public SubThread(User user) { this.user=user; } @Override public void run() { user.SetValue("ww","456"); } } static class User { private String name="ylc"; private String pwd="123"; public void GetName() { System.out.println(Thread.currentThread().getName()+"==>"+name+"password"+pwd); } public void SetValue(String name,String pwd) { System.out.println("Originally name="+this.name+",pwd="+this.pwd); this.name=name; this.pwd=pwd; System.out.println("Update to name="+name+",pwd="+pwd); } } }
When the data modification is not completed, the original data is read instead of the modified data
The reason for dirty reading is that the modification and reading of shared data are not synchronized
The solution is to synchronize the modified and read methods, and add the synchronized keyword to the method
An exception occurred in the thread to release the lock
If an exception occurs to one thread in the synchronization method, will the lock not be released, and other waiting threads are waiting all the time? Demonstration:
public class SynchronizedLock { public static void main(String[] args) { SynchronizedLock synchronizedLock=new SynchronizedLock(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm(); } }.start(); new Thread(new RunnableThread()) { @Override public void run() { synchronizedLock.mm2(); } }.start(); } //Synchronous instance method public synchronized void mm() { for (int i = 0; i <100 ; i++) { if(i==50) { Integer.parseInt("abc");//Exception setting } System.out.println(Thread.currentThread().getName()+"-->"+i); } } public void mm2() { synchronized (this) { for (int i = 0; i <100 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } }
If an exception occurs to a thread during synchronization, the lock object will be automatically released for the next thread to continue execution
deadlock
Multiple locks may need to be used in multithreading. If the order of obtaining locks is inconsistent, it may lead to deadlock.
public class Text06_5 { public static void main(String[] args) { SubThread subThread=new SubThread(); SubThread subThread2=new SubThread(); subThread.setName("a"); subThread2.setName("b"); subThread.start();subThread2.start(); } static class SubThread extends Thread { private static final Object lock1=new Object(); private static final Object lock2=new Object(); @Override public void run() { if("a".equals(Thread.currentThread().getName())) { synchronized (lock1) { System.out.println("a thread lock1 Got the lock,Need to get lock2"); synchronized (lock2) { System.out.println("a thread lock2 Got the lock"); } } } if("b".equals(Thread.currentThread().getName())) { synchronized (lock2) { System.out.println("b thread lock2 Got the lock,Need to get lock1"); synchronized (lock1) { System.out.println(" b thread lock1 Got the lock"); } } } } } }
The program is still running, but it enters a stuck state. Thread a gets lock1. To release the thread, execute the following code to obtain lock2, but lock2 cannot be released by thread b. there is a conflict between Snipes and clams.
Avoid deadlock: when it is necessary to obtain a lock, all threads obtain the lock in the same order. Thread a locks lock1 first and then lock2. Similarly, thread b will not have a deadlock.
Original link:
https://www.cnblogs.com/cg-ww/p/14491507.html?utm_source=tuicool&utm_medium=referral
For more information, you can pay attention to the official account "w's programming diary" and reply to Java for more information.