Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

Author’s other platforms:

| CSDN:blog.csdn.net/qq_4115394…

| the nuggets: juejin. Cn/user / 651387…

| zhihu: www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

| public no. : 1024 notes

This article is about 15,558 words and takes 35 minutes to read

1 introduction

The last article talked about the related content of Synchronized in multithreaded programming, Synchronized in addition to the synchronization method can also synchronize statement blocks, this article will introduce how Synchronized statement blocks.

2 the body

1. Disadvantages of Synchronized

Before introducing the Synchronized blocks, let’s talk about the drawbacks of the Synchronized approach. First look at the code:

public class Demo9 { public static void main(String[] args) throws InterruptedException { Demo9Service service = new Demo9Service(); Thread t1 = new Demo9ThreadA(service); t1.setName("A"); Thread t2 = new Demo9ThreadA(service); t2.setName("B"); t1.start(); t2.start(); Thread.sleep(20000); long start = Demo9Untils.start1 > Demo9Untils.start2 ? Demo9Untils.start2 : Demo9Untils.start1; long end = Demo9Untils.end1 > Demo9Untils.end2 ? Demo9Untils.end1 : Demo9Untils.end2; System.out.println(" total time: "+ (end-start) / 1000 +" seconds "); } } class Demo9Untils{ static long start1; static long start2; static long end1; static long end2; } class Demo9Service{synchronized public void foo(){try{system.out.println (" start task "); Thread.sleep(5000); Println (" thread.currentThread ().getName() "); System.out.println(" End task "); }catch(InterruptedException e){ e.printStackTrace(); } } } class Demo9ThreadA extends Thread{ public Demo9Service service; public Demo9ThreadA(Demo9Service service){ this.service = service; } @Override public void run() { Demo9Untils.start1 = System.currentTimeMillis(); service.foo(); Demo9Untils.end1 = System.currentTimeMillis(); } } class Demo9ThreadB extends Thread{ public Demo9Service service; public Demo9ThreadB(Demo9Service service){ this.service = service; } @Override public void run() { Demo9Untils.start2 = System.currentTimeMillis(); service.foo(); Demo9Untils.end2 = System.currentTimeMillis(); }}Copy the code

Results:

2. Synchronized code blocks

Let’s look at the code for synchronized blocks:

public class Demo10 { public static void main(String[] args) { Demo10Service service = new Demo10Service(); Thread t1 = new Demo10Thread(service); t1.setName("A"); t1.start(); Thread t2 = new Demo10Thread(service); t2.setName("B"); t2.start(); } } class Demo10Service{ public void foo(){ try { synchronized (this) { System.out.println(thread.currentThread ().getName() + "start with" + system.currentTimemillis ()); Thread.sleep(2000); System.out.println(thread.currentThread ().getName() + "end at" + system.currentTimemillis ()); } }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo10Thread extends Thread{ private Demo10Service service; public Demo10Thread(Demo10Service service){ this.service = service; } @Override public void run() { service.foo(); }}Copy the code

Results:

From the above example, you can see that the Synchronized synchronization method takes too long.

3. Use synchronized code blocks to solve the same block method problem

So how to use synchronous code block to solve the problem of synchronous method, the code is as follows:

public class Demo11 { public static void main(String[] args) throws InterruptedException { Demo11Service service = new Demo11Service(); Thread t1 = new Demo11ThreadA(service); t1.setName("A"); t1.start(); Thread t2 = new Demo11ThreadB(service); t2.setName("B"); t2.start(); Thread.sleep(10000); long start = Demo11Utils.start1 > Demo11Utils.start2 ? Demo11Utils.start2 : Demo11Utils.start1; long end = Demo11Utils.end1 > Demo11Utils.end2 ? Demo11Utils.end1 : Demo11Utils.end2; System.out.println(" time: "+ (end-start) /1000 +" seconds "); } } class Demo11Utils{ static long start1; static long start2; static long end1; static long end2; } class Demo11Service{ /*synchronized*/ public void foo(){ try { System.out.println(Thread.currentThread().getName() + "Start the mission "); Thread.sleep(3000); Synchronized (this) {system.out.println (thread.currentThread ().getName() + "synchronized "); } system.out.println (thread.currentThread ().getName() + "end task "); }catch(InterruptedException e){ e.printStackTrace(); } } } class Demo11ThreadA extends Thread{ private Demo11Service service; public Demo11ThreadA(Demo11Service service){ this.service = service; } @Override public void run() { Demo11Utils.start1 = System.currentTimeMillis(); service.foo(); Demo11Utils.end1 = System.currentTimeMillis(); } } class Demo11ThreadB extends Thread{ private Demo11Service service; public Demo11ThreadB(Demo11Service service){ this.service = service; } @Override public void run() { Demo11Utils.start2 = System.currentTimeMillis(); service.foo(); Demo11Utils.end2 = System.currentTimeMillis(); }}Copy the code

Results:

It can be seen that the time is shortened significantly!

4. Synchronized code blocks

Public class Demo13 {public static void main(String[] args) throws InterruptedException { DemoService13 service13 = new DemoService13(); Thread t1 = new Demo13Thread1(service13); t1.start(); Thread.sleep(100); Thread t2 = new Demo13Thread2(service13); t2.start(); Public void foo1(){synchronized (this){// synchronized (this){// synchronized (this){// synchronized (this){// synchronized (this){ System.out.println("foo1 method start time: "+ system.currentTimemillis ()); Thread.sleep(2000); System.out.println("foo1: "+ system.currentTimemillis ()); } catch (InterruptedException e) { e.printStackTrace(); }}} public void foo2(){synchronized (this){system.out.println ("foo2: "+ system.currentTimemillis ()); System.out.println("foo2: "+ system.currentTimemillis ()); }}} class Demo13Thread1 extends Thread{private DemoService13 service13; Public Demo13Thread1(DemoService13 service){this.service13 = service; } public void run(){ service13.foo1(); }} class Demo13Thread2 extends Thread{private DemoService13 service13; Public Demo13Thread2(DemoService13 service){this.service13 = service; } public void run(){ service13.foo2(); }}Copy the code

Results:

When using synchronized(this), note that when one thread accesses a synchronized(this) block of an object, access from other threads to the synchronized(this) block is blocked. Note Synchronized uses the same object lock.

The synchroinze(this) block locks the current object. Such as:

/** *synchroinze(this) blocks the current object */ public class Demo14 {public static void main(String[] args) throws InterruptedException { Demo14Service service = new Demo14Service(); Thread t1 = new Demo14ThreadA(service); Thread t2 = new Demo14ThreadB(service); t2.start(); Thread.sleep(10); t1.start(); }} class Demo14Service{synchronized public void foo1(){system.out.println ("foo1 is running "); } public void foo2(){try {synchronized (this) {system.out.println ("foo2 method start "); Thread.sleep(2000); System.out.println("foo2 method ends "); } }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo14ThreadA extends Thread{ private Demo14Service service; public Demo14ThreadA(Demo14Service service){ this.service = service; } @Override public void run() { service.foo1(); } } class Demo14ThreadB extends Thread{ private Demo14Service service; public Demo14ThreadB(Demo14Service service){ this.service = service; } @Override public void run() { service.foo2(); }}Copy the code

Results:

Synchronized (this) block Like synchronized methods, a synchronized(this) block locks the current object.

5. Use arbitrary objects as object locks

In addition to synchronizing blocks of code using syncrhonized(this), Java also supports synchronizing arbitrary objects as object locks. This arbitrary object is a parameter in a member variable or method. The syntax is:

synchronized(lockobj)
Copy the code

In the case of multiple threads holding the same object lock, only one thread can execute the code in the Synchoronized (lockobj) synchronized code block at a time. If not the same object lock is used, the result of the run is an asynchronous call, with the result printed across.

Such as:

Public class Demo15 {public static void main(String[] args) {Demo15Service service = new Demo15Service(); Thread t1 = new Demo15Thread(service); T1. Elegantly-named setName (" thread 1 "); t1.start(); Thread t2 = new Demo15Thread(service); T2. Elegantly-named setName (" thread 2 "); t2.start(); }} Class Demo15Service{private Object lockObject = new Object(); Public void foo(){//synchronized synchronized (lockObject){try { System.out.println(thread.currentThread ().getName() + "start with" + system.currentTimemillis ()); Thread.sleep(2000); System.out.println(thread.currentThread ().getName() + "end at" + system.currentTimemillis ()); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Demo15Thread extends Thread{ private Demo15Service service; public Demo15Thread(Demo15Service service){ this.service = service; } @Override public void run() { service.foo(); }}Copy the code

Results:

Non-tihs objects have certain advantages over synchronized (this).

A class has many synchrnoized methods, which can achieve synchronization, but will be blocked, affecting the running efficiency. If the synchronized code block is not this object, the program and synchronized method in the synchronized code block are not competing for this lock with other locks (this), which greatly improves the efficiency of operation.

Public class Demo16 {public static void main(String[] args) throws InterruptedException { Demo16Service service = new Demo16Service(); Thread t1 = new Demo16ThreadA(service); t1.start(); Thread.sleep(10); Thread t2 = new Demo16ThreadB(service); t2.start(); } } class Demo16Service{ private Object lockObject = new Object(); Public void foo(){try {synchronized (lockObject) {system.out.println ("foo start time "+ system.currentTimemillis ()); Thread.sleep(2000); System.out.println("foot method end time "+ system.currentTimemillis ()); } }catch (InterruptedException e){ e.printStackTrace(); }} synchronized public void foo2(){system.out.println ("foo2 "+ system.currentTimemillis ()); System.out.println("foo2 method end time "+ system.currentTimemillis ()); } } class Demo16ThreadA extends Thread{ private Demo16Service service; public Demo16ThreadA(Demo16Service service){ this.service =service; } @Override public void run() { service.foo(); } } class Demo16ThreadB extends Thread{ private Demo16Service service; public Demo16ThreadB(Demo16Service service){ this.service = service; } @Override public void run() { service.foo2(); }}Copy the code

Results:

Synchronized (not this object) to solve dirty read

import java.util.ArrayList; import java.util.List; public class Demo17 { public static void main(String[] args) throws InterruptedException { Demo17List list = new Demo17List(); Thread t1 = new Demo17ThreadA(list); t1.start(); Thread t2 = new Demo17ThreadB(list); t2.start(); Thread.sleep(5000); System.out.println("list size is " + list.size()); } } class Demo17List{ private List list = new ArrayList(); synchronized public void add(Object obj){ list.add(obj); } synchronized public int size(){ return list.size(); } } class Demo17Service{ private Object lockObject = new Object(); public void add(Demo17List list, Object obj){ try { synchronized (list) { if (list.size() < 1) { Thread.sleep(2000); list.add(obj); } } }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo17ThreadA extends Thread{ private Demo17List list; public Demo17ThreadA(Demo17List list){ this.list = list; } @Override public void run() { Demo17Service service = new Demo17Service(); service.add(list, "a"); } } class Demo17ThreadB extends Thread{ private Demo17List list; public Demo17ThreadB(Demo17List list){ this.list = list; } @Override public void run() { Demo17Service service = new Demo17Service(); service.add(list, "b"); }}Copy the code

Results:

Synchronized (lockobj) format is written as the object lock lockobj itself, summed up the above results so as to draw the following conclusions:

A. The syncrhonized(lockobj) synchronization effect occurs when multiple threads simultaneously execute a synchronized code block;

B. When other threads execute the synchronized method of the lockobj object, the synchronized effect is also synchronized;

C. Synchronize when another thread executes the Synchronize (this) block in the lockobj object method

If another thread calls a method without the synchronized keyword, the synchronized call is still made asynchronously.

6. Semi-asynchronous and semi-synchronous

Synchronization and asynchrony in concurrent mode are completely different concepts from synchronization and asynchrony in IO model.

In the IO model, the difference between synchronous and asynchronous is what IO events are notified to the application by the kernel (ready or complete), and who should do the IO reading and writing (application or kernel).

In concurrent mode, synchronous means that the program is executed exactly in the order of the code sequence, and asynchronous means that the execution of the program needs to be driven by system events. Common system events include interrupt signals, etc.

Threads that run synchronously are called synchronous threads, and threads that run asynchronously are called asynchronous threads. Obviously, asynchronous thread has high execution efficiency and strong real-time performance, which is the model adopted by many embedded programs. But writing programs that execute asynchronously is complex, difficult to debug and extend, and not suitable for large amounts of concurrency. On the contrary, synchronous thread, although it is relatively low efficiency, poor real-time, but simple logic. Therefore, for some applications that require both good real-time performance and simultaneous processing of multiple customer requests, both synchronous and asynchronous threads can be used. That is to use semi-synchronous/semi-asynchronous mode to achieve!

If synchronized is used, the code is as follows:

Public class Demo12 {public static void main(String[] args) {Demo12Service service = new Demo12Service(); Thread t1 = new Demo12Thread(service); t1.setName("A"); t1.start(); Thread t2 = new Demo12Thread(service); t2.setName("B"); t2.start(); } } class Demo12Service { public void foo(){ try{ for (int i = 0; i < 100; Thread.out.println (" thread.currentThread ().getName() + ", I =" + I); Thread.sleep(10); } System.out.println(); synchronized (this){ for (int i = 0; i < 100; I ++) {system.out.println (" thread.currentThread ().getName() + ", I =" + I); Thread.sleep(10); } } }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo12Thread extends Thread{ private Demo12Service service; public Demo12Thread(Demo12Service service){ this.service = service; } @Override public void run() { service.foo(); }}Copy the code

Results:

Code not in a synchronized block is executed asynchronously, and code in a Synchroinzed block is executed synchronously.

Asynchronous output is interleaved, while synchronous threads do not execute thread B until thread A finishes executing.

Synchronized static methods and synchronized(class) code blocks

Synchronized can also be used to modify static methods. A static method is used to lock the Class corresponding to an object’s current *. Java file. Such as:

public class Demo18 { public static void main(String[] args) { Thread t1 = new Demo18ThreadA(); t1.setName("A"); t1.start(); Thread t2 = new Demo18ThreadB(); t2.setName("B"); t2.start(); } } class Demo18Service{ synchronized public static void foo1(){ System.out.println(Thread.currentThread().getName() + "Enter method foo1 in" + system.currentTimemillis ()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end method foo1 in" + system.currentTimemillis ()); } synchronized public static void foo2(){system.out.println (thread.currentThread ().getName() + System.currentTimeMillis()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end method foo2 in" + system.currentTimemillis ()); } } class Demo18ThreadA extends Thread{ @Override public void run() { Demo18Service.foo1(); } } class Demo18ThreadB extends Thread{ @Override public void run() { Demo18Service.foo2(); }}Copy the code

Results:

It can be seen from the above results that the use of synchronized modifiers on static methods is consistent with the use of synchronized modifiers on non-static methods. Synchronized locks a Class on a static method, while syncrhonized locks an object on a non-static method.

public class Demo19 { public static void main(String[] args) { Demo19Service service = new Demo19Service(); Thread t1 = new Demo19ThreadA(service); t1.setName("A"); t1.start(); Thread t2 = new Demo19ThreadB(service); t2.setName("B"); t2.start(); } } class Demo19Service{ synchronized public static void foo1(){ System.out.println(Thread.currentThread().getName() + "Enter foo1 method in" + System.currentTimemillis ()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end foo1 method in" + system.currentTimemillis ()); } synchronized public void foo2(){system.out.println (thread.currentThread ().getName() + System.currentTimeMillis()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end foo2 method in" + system.currentTimemillis ()); } } class Demo19ThreadA extends Thread{ private Demo19Service service; public Demo19ThreadA(Demo19Service service){ this.service = service; } @Override public void run() { service.foo2(); } } class Demo19ThreadB extends Thread{ private Demo19Service service; public Demo19ThreadB(Demo19Service service){ this.service = service; } @Override public void run() { service.foo1(); }}Copy the code

Results:

The reason the above works asynchronously is because holding locks is different. Non-static methods hold the object lock, while static methods hold the Class lock, which can be applied to all object instances of a Class.

Synchronized (class) blocks of code serve the same purpose as synchronized static methods

public class Demo20 { public static void main(String[] args) { Demo20Service service = new Demo20Service(); Thread t1 = new Demo20ThreadA(service); t1.setName("A"); t1.start(); Thread t2 = new Demo20ThreadA(service); t2.setName("B"); t2.start(); } } class Demo20Service{ synchronized public static void foo1(){ System.out.println(Thread.currentThread().getName() + "Enter foo1 method in" + System.currentTimemillis ()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end foo1 method in" + system.currentTimemillis ()); } public static void foo2(){ synchronized (Demo20Service.class) { System.out.println(Thread.currentThread().getName() + "Enter foo1 method in" + System.currentTimemillis ()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (thread.currentThread ().getName() + "end foo1 method in" + system.currentTimemillis ()); } } } class Demo20ThreadA extends Thread{ private Demo20Service service; public Demo20ThreadA(Demo20Service service){ this.service = service; } @Override public void run() { service.foo1(); } } class Demo20ThreadB extends Thread{ private Demo20Service service; public Demo20ThreadB(Demo20Service service){ this.service = service; } @Override public void run() { service.foo2(); }}Copy the code

Results:

8, synchronized * (String)

When String is locked using the synchronized keyword:

public class Demo21 { public static void main(String[] args) { Thread t1 = new Demo21ThreadA(); t1.setName("A"); t1.start(); Thread t2 = new Demo21ThreadB(); t2.setName("B"); t2.start(); } } class Demo21Service{ public static void foo1(String lockObject){ try { synchronized (lockObject) { while (true) { System.out.println(" Thread "+ thread.currentThread ().getName()); Thread.sleep(1000); } } }catch (InterruptedException e){ e.printStackTrace(); }} public static void foo2(Object lockObject){try {synchronized (lockObject) {while (true) {system.out.println (" thread ")  + Thread.currentThread().getName()); Thread.sleep(1000); } } }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo21ThreadA extends Thread{ @Override public void run() { Demo21Service.foo1("AA"); } } class Demo21ThreadB extends Thread{ @Override public void run() { Demo21Service.foo2("AA"); }}Copy the code

Results:

From the above results, you can see that only thread B is printed. The reason why only thread B is running is that the object lock of both threads is AA, and both threads hold the same lock, so thread B can run. This is the problem with String constants. So instead of using string types for Object locks, use other types, such as New Object(), and the Object will not be placed in the cache.

3 summary

This article mainly discusses the role of Synchronized keyword in multithreaded programming for synchronization mechanism. The Synchronized keyword modifies not only methods but code blocks as well.

If you think this article is good and helpful, just like it and share it with more people!

Related recommendations:

  • Common apis for multithreaded programming threads

  • Several methods of stopping threads in multithreaded programming

  • How to suspend and resume threads in multithreaded programming

  • Use of thread pools for multithreaded programming

  • Thread synchronization in multithreaded programming (part 1) : Synchronized method