Article to understand synchronized high concurrency keywords

Pre knowledge: 1. What is thread safety? It is very complicated to give an exact definition of thread safety. First, we ...
1. Introduction to Synchronized
2. What happens if you don't use Synchronized
3. Two uses of Synchronized
4. Properties of Synchronized
5. Principle of locking and releasing lock
6. Disadvantages of Synchronized
7. Common interview questions
8. Summary

Pre knowledge:

1. What is thread safety?

It is very complicated to give an exact definition of thread safety.
First, we can search many "definitions" on the Internet, such as:
It can be invoked in multiple threads and there will be no erroneous interaction between threads.
... can be called by multiple threads at the same time without the caller performing additional actions.
If you look at these definitions, it's hard not to be confused about thread safety. If a class can be safely used in multithreading, it is a thread safe class.
We can think of it as follows:
When multiple threads access a class, regardless of the scheduling mode adopted by the runtime environment or how these threads will execute alternately, and this class can show correct behavior without any additional synchronization or cooperation in the calling code, this class is called thread safe.

2. What is atomicity?

Simply speaking, atomicity means that an operation can not be suspended and dispatches in the middle of cpu, that is, it can not be interrupted, otherwise it will not be executed. If an operation is atomicity, then there will be no strange problems such as variable modification in multi thread environment.
For example, the increment operation + + count, although it is a compact syntax, makes it look like an operation, but this operation is not atomic, so it will not be executed as an inseparable operation. In fact, it includes three independent operations, reading the value of count, adding 1 to the value, and then writing the calculation result to count, which is a "read modify write" If two threads perform an increment operation on a counter at the same time without synchronization, there will be a numerical deviation.

3. What built-in lock?

java provides a built-in lock mechanism to support atomicity and synchronization code blocks (explained in usage below). Each java object can be used as a lock to achieve synchronization. These locks are called built-in locks or monitor locks. The thread will automatically obtain the lock before entering the synchronous code block, and automatically release the lock when exiting the synchronous code block, whether exiting through the normal control path or throwing an exception from the code block. The only way to obtain a built-in lock is to enter a synchronized code block or method protected by the lock.

4. What is visibility?

Visibility is a complex property because errors in visibility always go against our intuition. In a single thread, if a variable writes a value first and then reads the variable without other write operations, the same value can always be obtained. However, this may sound uncomfortable when read and write operations are executed in different threads. Usually, we can't ensure that the thread executing the read operation can see the values written by other threads in time. Sometimes it's even impossible. In order to ensure the visibility of memory writes between multiple threads, a synchronization mechanism must be used.

1. Introduction to Synchronized

In java programming, synchronization mechanism is often used, and Synchronized is used most. It is a keyword in java and can be used to ensure atomicity, visibility and order.

2. What happens if you don't use Synchronized

Example: for two threads, we decrement a number until the number is not greater than 0!

package synchronize; /** * @author delingw * @version 1.0 * What happens when the synchronized keyword is not used * Example: for two threads, we decrement a number until the number is not greater than 0! */ public class SynchronizedNoUse implements Runnable { int num = 10; @Override public void run() { System.out.println(Thread.currentThread().getName() + " Enter thread!"); while (num > 0) { System.out.println(Thread.currentThread().getName() + " num=" + num); num--; } System.out.println(Thread.currentThread().getName() + " Exit thread!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse = new SynchronizedNoUse(); // Thread one Thread thread1 = new Thread(synchronizedNoUse); // Thread two Thread thread2 = new Thread(synchronizedNoUse); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }

result:

Thread-0 Enter thread! Thread-1 Enter thread! Thread-0 num=10 Thread-1 num=10 Thread-1 num=8 Thread-1 num=7 Thread-1 num=6 Thread-1 num=5 Thread-1 num=4 Thread-1 num=3 Thread-1 num=2 Thread-1 num=1 Thread-1 Exit thread! Thread-0 num=9 Thread-0 Exit thread!

Through the results, we will know that there is an error in the data.
The reason is: num – although it is a line of code, it actually contains at least the following three actions:
1. Read the value of num
2. Calculate num-1
3. Write the calculation result of num-1 back to memory and assign num
Because these three steps are not atomic, that is, they are not done at one go, which leads to thread insecurity!
If we add a lock, we will solve this problem. The reason is that at the same time, only one thread can use a lock, and other threads must wait.

package synchronize; /** * @author delingw * @version 1.0 * What happens when the synchronized keyword is not used * Example: for two threads, we decrement a number until the number is not greater than 0! */ public class SynchronizedNoUse implements Runnable { int num = 10; @Override public synchronized void run() { System.out.println(Thread.currentThread().getName() + " Enter thread!"); // While (Num > 0) {/ / cancel while to prevent other threads from getting System.out.println(Thread.currentThread().getName() + " num=" + num); num--; // } System.out.println(Thread.currentThread().getName() + " Exit thread!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse = new SynchronizedNoUse(); // Thread one Thread thread1 = new Thread(synchronizedNoUse); // Thread two Thread thread2 = new Thread(synchronizedNoUse); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }

result:

Thread-0 Enter thread! Thread-0 num=10 Thread-0 Exit thread! Thread-1 Enter thread! Thread-1 num=9 Thread-1 Exit thread!

3. Two uses of Synchronized

Object lock

What is an object lock?

When the same object calls synchronized code blocks and methods in multiple threads, it needs to obtain the object lock. The object lock of an object is unique, and only one thread can get it. Therefore, when a thread is executing synchronized methods, other threads need to wait or block

Two methods of object lock implementation 1. Code block

Note: the default lock for synchronized is this

package synchronize; /** * @author delingw * @version 1.0 * Object lock code block */ public class SynchronizedNoUse implements Runnable { @Override public void run() { synchronized (this) { System.out.println("I'm an object lock" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse); Thread t2 = new Thread(synchronizedNoUse); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm an object lock Thread-0 Thread-0 End of operation! I'm an object lock Thread-1 Thread-1 End of operation! end of execution!
2. Methodologically
package synchronize; /** * @author delingw * @version 1.0 * Object lock method */ public class SynchronizedNoUse implements Runnable { @Override public synchronized void run() { System.out.println("I'm an object lock" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse); Thread t2 = new Thread(synchronizedNoUse); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm an object lock Thread-0 Thread-0 End of operation! I'm an object lock Thread-1 Thread-1 End of operation! end of execution!

Class lock

What is a class lock?

Different objects of a class call statically synchronized code blocks in multiple threads. Class locks need to be obtained when using methods. Class locks of a class are unique,
Only one thread can get it, so when an object executes the synchronized method in the thread, the objects of other threads need to enter the blocking queue and wait

Two ways to implement class lock 1. Code block
package synchronize; /** * @author delingw * @version 1.0 * Class lock code block */ public class SynchronizedNoUse implements Runnable { @Override public void run() { synchronized (SynchronizedNoUse.class) { classBlock(); } } private static void classBlock() { System.out.println("I'm a class lock" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse2); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm a class lock Thread-0 Thread-0 End of operation! I'm a class lock Thread-1 Thread-1 End of operation! end of execution!
2. Methodologically
package synchronize; /** * @author delingw * @version 1.0 * Class lock method */ public class SynchronizedNoUse implements Runnable { @Override public void run() { classBlock(); } private synchronized static void classBlock() { System.out.println("I'm a class lock" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse2); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm a class lock Thread-0 Thread-0 End of operation! I'm a class lock Thread-1 Thread-1 End of operation! end of execution!

The difference between object lock and class lock

: one is an object lock (only one object), and the other is a class lock (there can be multiple objects, but only one class file).

4. Properties of Synchronized

Reentrant

When a thread requests a lock held by another thread, the requesting thread will block. However, because the built-in lock is reentrant, if a thread tries to obtain a lock that is already held by itself, the request will succeed. "Reentry" " An implementation method of is to associate a get count value or an owner thread for each lock. When the count value is 0, the lock is considered not to be held by any thread. When a thread requests a lock that is not held, the JVM will record the lock holder and set the get count value to 1. If the same thread gets the lock again, the count value will be incremented, and when the thread exits When synchronizing code blocks, the counter will decrease accordingly. When the count value is 0, this lock will be released!

Non interruptibility

Non interruptibility is that when a thread obtains a lock, if other threads want to obtain the lock, they can only wait until the thread holding the lock executes and then obtain the lock. During this period, the thread cannot be interrupted.

5. Principle of locking and releasing lock

Timing of lock acquisition and release: entering and exiting synchronous code blocks (including throwing exceptions)

6. Disadvantages of Synchronized

Everything has its disadvantages: let's talk about its disadvantages,
1. Low efficiency: the release of the lock is less, it cannot be interrupted, and the timeout cannot be set
2. It is not flexible enough, locking and releasing locks are single, and each lock has only a single condition, which may not be enough.

7. Common interview questions

Question 1: can a thread access the synchronization method of an object at the same time?

No, you need to wait in line. Because the same object needs to obtain an object lock, and there is only one object lock

package synchronize; /** * @author delingw * @version 1.0 * Can multiple threads access the synchronization method of an object at the same time? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { classBlock(); } private synchronized void classBlock() { System.out.println("I'm the synchronization method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse1); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm the synchronization method Thread-0 Thread-0 End of operation! I'm the synchronization method Thread-1 Thread-1 End of operation! end of execution!
Question 2: can a thread access the synchronization method of two objects at the same time?

They can be accessed at the same time because they are not the same object and do not need to grab the object lock.

package synchronize; /** * @author delingw * @version 1.0 * Can multiple threads access the synchronization method of two objects at the same time? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { classBlock(); } private synchronized void classBlock() { System.out.println("I'm the sync 1 method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse2); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm the sync 1 method Thread-0 I'm the sync 1 method Thread-1 Thread-1 End of operation! Thread-0 End of operation! end of execution!
Question 3: thread access to synchronized static methods?

You need to queue because it is a class lock

package synchronize; /** * @author delingw * @version 1.0 * synchronized static methods accessed by multiple threads? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { classBlock(); } private synchronized static void classBlock() { System.out.println("I'm a static synchronization method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse2); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm a static synchronization method Thread-0 Thread-0 End of operation! I'm a static synchronization method Thread-1 Thread-1 End of operation! end of execution!
Question 4: can threads access synchronous and asynchronous methods at the same time?

Synchronous methods can only be called by one object at a time. Asynchronous methods have no restrictions and can be called at the same time

package synchronize; /** * @author delingw * @version 1.0 * Can threads access synchronous and asynchronous methods at the same time? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")) { classBlock1(); } classBlock2(); } private synchronized void classBlock1() { System.out.println("I'm the synchronization method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } private synchronized static void classBlock2() { System.out.println("I am an asynchronous method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); // SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse1); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm the synchronization method Thread-0 I am an asynchronous method Thread-1 Thread-1 End of operation! Thread-0 End of operation! I am an asynchronous method Thread-0 Thread-0 End of operation! end of execution!
Question 5: can threads access different common synchronization methods of the same object?

No, because when two threads call different common synchronization methods, they need to obtain the lock first, and the default lock of synchronized is this, so it is still an object lock, so it is queued

package synchronize; /** * @author delingw * @version 1.0 * Can threads access different common synchronization methods of the same object? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")) { classBlock1(); } classBlock2(); } private synchronized void classBlock1() { System.out.println("I'm synchronization method 1" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } private synchronized void classBlock2() { System.out.println("I'm synchronization method 2" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); // SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse1); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm synchronization method 1 Thread-0 Thread-0 End of operation! I'm synchronization method 2 Thread-0 Thread-0 End of operation! I'm synchronization method 2 Thread-1 Thread-1 End of operation! end of execution!
Question 6: can a thread access both static synchronized and non static synchronized methods?

Static is a class lock and non static is an object lock, so it can be executed at the same time

package synchronize; /** * @author delingw * @version 1.0 * Can threads access both static and non static synchronized methods? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")) { classBlock1(); } classBlock2(); } private synchronized static void classBlock1() { System.out.println("I'm a static synchronization method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } private synchronized void classBlock2() { System.out.println("I am a non static synchronization method" + Thread.currentThread().getName()); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse2); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm a static synchronization method Thread-0 I am a non static synchronization method Thread-1 Thread-0 End of operation! Thread-1 End of operation! I am a non static synchronization method Thread-0 Thread-0 End of operation! end of execution!
Question 7: will the method throw an exception and release the lock?

Yes, the lock will be released immediately after the exception is thrown

package synchronize; /** * @author delingw * @version 1.0 * Method throws an exception. Will the lock be released? */ public class SynchronizedNoUse implements Runnable { @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")) { classBlock1(); } classBlock2(); } private synchronized void classBlock1() { System.out.println("I'm locking method 1 " + Thread.currentThread().getName()); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } throw new RuntimeException(); // System.out.println(Thread.currentThread().getName() + "end of run!"); } private synchronized void classBlock2() { System.out.println("I'm locking method 2 " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "End of operation!"); } public static void main(String[] args) throws InterruptedException { SynchronizedNoUse synchronizedNoUse1 = new SynchronizedNoUse(); // SynchronizedNoUse synchronizedNoUse2 = new SynchronizedNoUse(); Thread t1 = new Thread(synchronizedNoUse1); Thread t2 = new Thread(synchronizedNoUse1); t1.start(); t2.start(); // results of enforcement while (t1.isAlive() || t2.isAlive()) { } System.out.println("end of execution!"); } }

result:

I'm locking method 1 Thread-0 I'm locking method 2 Thread-1 Exception in thread "Thread-0" java.lang.RuntimeException at synchronize.SynchronizedNoUse.classBlock1(SynchronizedNoUse.java:24) at synchronize.SynchronizedNoUse.run(SynchronizedNoUse.java:12) at java.lang.Thread.run(Thread.java:748) Thread-1 End of operation! end of execution!

8. Summary

The JVM will automatically lock and unlock by using monitor to ensure that only one thread can execute the specified code at the same time, so as to ensure thread safety. Meanwhile, synchronized also has reentrant and non interruptible properties.

3 October 2021, 14:36 | Views: 4465

Add new comment

For adding a comment, please log in
or create account

0 comments