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 12,425 words and takes 30 minutes to read

1 introduction

Synchronization in multithreading refers to how to develop a thread-safe program or application, that is, to solve some related problems caused by non-thread-safe —– dirty read.

Thread safety and non – thread safety is the learning of multithreaded programming and daily development will encounter problems. Non-thread-safe is when multiple threads access a member variable in the same object. The result is dirty reads, in which the data retrieved has actually been changed. Thread-safe means that the value of a member variable is synchronized to avoid dirty reads.

When it comes to thread synchronization, it’s important to mention the Synchronized keyword in Java.

The Synchronized keyword is a type of lock that addresses the synchronization of access to resources between multiple threads. The Synchronized keyword ensures that only one thread can execute a method or code block that it modifies at any time. Other threads must wait for the current thread to finish executing the method (code block) before executing it.

So the Synchronized keyword serves two purposes:

1. Synchronized

2. Synchronized code blocks

This is a long one, so I’m going to write it in two parts. In this article, I will write about Synchronized.

The contents of this chapter include:

Local variables are thread-safe

2. Member variables are not thread-safe

3. Multiple objects use multiple object locks

4. Synchronized methods lock the entire object

5, dirty reads

6. Lock reentrant

7. Automatic release of locks

Synchronization does not have inheritance

2 the body

Local variables are thread-safe

Local variables do not have a non-thread-safe problem. They are always thread-safe because local variables are private.

That is, thread A modifies a local variable without affecting thread B’s access. Such as:

package com.jiangxia.chap2; /** * Jiangxia * author: Jiangxia * date: 2021-04-05 */ public class Demo1 { public static void main(String[] args) { Servie1 servie1 = new Servie1(); Thread t1 = new Thread1(servie1); t1.start(); Thread t2 = new Thread2(servie1); t2.start(); } } class Servie1{ public void set(String string){ int num = 0; if("a".equals(string)){ num = 100; System.out.println("a set"); Try {// The purpose of sleep is to wait for another Thread to change the value of num thread.sleep (2000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ num = 200; System.out.println("b set"); } System.out.println("string="+string+"; num="+num); } } class Thread1 extends Thread{ private Servie1 servie1; public Thread1(Servie1 servie1){ this.servie1 = servie1; } @Override public void run() { servie1.set("a"); } } class Thread2 extends Thread{ private Servie1 servie1; public Thread2(Servie1 servie1){ this.servie1 = servie1; } @Override public void run() { servie1.set("b"); }} Copy the codeCopy the code

2. Member variables are not thread-safe

Member variables are not thread-safe in Java. By the way, what are member variables and local variables?

Local variables: Variables defined within a method are called “local variables” or “temporary variables.” Memory occupied by local variables is freed after the method ends.

Member variables: Variables defined in the variables section of the class body.

Local variables and member variables differ mainly in their scope:

Member variables are inside the class;

Local variables are defined within their method body (or within a block within the method body).

Member variables may not be explicitly initialized; they may be set to default values by the system.

Local variables have no default values, so initial assignments must be set.

The location in memory is also different. Member variables are stored in heap memory after their class is instantiated. Local variables are stored in stack memory space when the local method is called.

Code:

package com.jiangxia.chap2; /** * Thread synchronization member variables are not thread-safe * author: jiangxia */ public class Demo2 { public static void main(String[] args) { Servie2 s = new Servie2(); Thread t1 = new ThreadA(s); t1.start(); Thread t2 = new ThreadB(s); t2.start(); } } class Servie2{ private int num = 0; public void set(String string){ num = 0; if("a".equals(string)){ num = 100; System.out.println("a set"); Try {// The purpose of sleep is to wait for another Thread to change the value of num thread.sleep (2000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ num = 200; System.out.println("b set"); } System.out.println("string="+string+"; num="+num); } } class ThreadA extends Thread{ private Servie2 servie2; public ThreadA(Servie2 servie2){ this.servie2 = servie2; } @Override public void run() { servie2.set("a"); } } class ThreadB extends Thread{ private Servie2 servie2; public ThreadB(Servie2 servie2){ this.servie2 = servie2; } @Override public void run() { servie2.set("b"); }} Copy the codeCopy the code

Results:

If the set method is set with the synchronized keyword, for example:

package com.jiangxia.chap2; /** * Thread synchronization member variables are not thread-safe * author: jiangxia */ public class Demo2 { public static void main(String[] args) { Servie2 s = new Servie2(); Thread t1 = new ThreadA(s); t1.start(); Thread t2 = new ThreadB(s); t2.start(); } } class Servie2{ private int num = 0; Synchronized public void set(String String){num = 0; if("a".equals(string)){ num = 100; System.out.println("a set"); Try {// The purpose of sleep is to wait for another Thread to change the value of num thread.sleep (2000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ num = 200; System.out.println("b set"); } System.out.println("string="+string+"; num="+num); } } class ThreadA extends Thread{ private Servie2 servie2; public ThreadA(Servie2 servie2){ this.servie2 = servie2; } @Override public void run() { servie2.set("a"); } } class ThreadB extends Thread{ private Servie2 servie2; public ThreadB(Servie2 servie2){ this.servie2 = servie2; } @Override public void run() { servie2.set("b"); }} Copy the codeCopy the code

Results:

Non-thread-safe problems can arise if two threads operate on a member variable in a business object at the same time, so you need to use the synchronized keyword before the method.

3. Multiple objects use multiple object locks

The lock acquired by Synchronized is an object lock, rather than a piece of code or method. Therefore, whichever thread executes the Synchronized keyword modification method first holds the lock on the object that the method belongs to, and other threads can only wait, assuming that multiple threads access the same object. If multiple threads access multiple objects, the JVM creates multiple object locks. Such as:

package com.jiangxia.chap2; public class Demo3 { public static void main(String[] args) { Servie3 s1 = new Servie3(); Servie3 s2 = new Servie3(); Thread t1 = new Demo3ThreadA(s1); Thread t2 = new Demo3ThreadB(s2); t1.start(); t2.start(); } } class Servie3{ private int num = 0; Synchronized public void set(String String){num = 0; if("a".equals(string)){ num = 100; System.out.println("a set"); Try {// The purpose of sleep is to wait for another Thread to change the value of num thread.sleep (2000); } catch (InterruptedException e) { e.printStackTrace(); } }else{ num = 200; System.out.println("b set"); } System.out.println("string="+string+"; num="+num); } } class Demo3ThreadA extends Thread{ private Servie3 servie3; public Demo3ThreadA(Servie3 servie3){ this.servie3 = servie3; } @Override public void run() { servie3.set("a"); } } class Demo3ThreadB extends Thread{ private Servie3 servie3; public Demo3ThreadB(Servie3 servie3){ this.servie3 = servie3; } @Override public void run() { servie3.set("b"); }} Copy the codeCopy the code

Results:

4. Synchronized methods lock the entire object

Such as:

package com.jiangxia.chap2; public class Demo4 { public static void main(String[] args) { Demo4Service service = new Demo4Service(); Thread t1 = new Demo4ThreadA(service); Thread t2 = new Demo4ThreadB(service); t1.start(); t2.start(); }} class Demo4Service{synchronized public void foo1(){system.out.println (" foo1 ", threadname: "+Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("foo1 method ends "); } synchronized public void foo2(){system.out.println (" thread.currentThread ().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("foo2 method ends "); } } class Demo4ThreadA extends Thread{ private Demo4Service service; public Demo4ThreadA(Demo4Service service){ this.service = service; } @Override public void run() { service.foo1(); } } class Demo4ThreadB extends Thread{ private Demo4Service service; public Demo4ThreadB(Demo4Service service){ this.service = service; } @Override public void run() { service.foo2(); }} Copy the codeCopy the code

Results:

Thread A first holds the object lock of the object object, and thread B cannot asynchronously call the method modified by the synchronized keyword of the object object. Thread B can only execute the object lock after the method execution of thread A is completed and released, that is, synchronous execution.

B threads can asynchronously call methods on object objects that do not use synchronized modifiers. Such as:

package com.jiangxia.chap2; public class Demo4 { public static void main(String[] args) { Demo4Service service = new Demo4Service(); Thread t1 = new Demo4ThreadA(service); Thread t2 = new Demo4ThreadB(service); t1.start(); t2.start(); }} class Demo4Service{synchronized public void foo1(){system.out.println (" foo1 ", threadname: "+Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("foo1 method ends "); } public void foo2(){system.out.println (" thread.currentThread ().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } system.out. println("foo2 method ends "); } } class Demo4ThreadA extends Thread{ private Demo4Service service; public Demo4ThreadA(Demo4Service service){ this.service = service; } @Override public void run() { service.foo1(); } } class Demo4ThreadB extends Thread{ private Demo4Service service; public Demo4ThreadB(Demo4Service service){ this.service = service; } @Override public void run() { service.foo2(); }} Copy the codeCopy the code

Results:

5, dirty reads

Dirty read indicates that multiple threads share the same object and modify the properties of the object. When reading the properties of the object, the properties are found to be modified by other threads.

Assuming A very simple example, when A shared object has A and B, there are setValue and geValue methods to modify and read these two values respectively. When setValue method is coupled with synchronized, that is to say, it is synchronized in the modification process, but getValue method is not synchronized. This means that when the first thread to execute the getValue method is finished, the two properties of the object, A and B, may be modified by the second thread to get the data that is not expected. This is A dirty read.

Such as:

package com.jiangxia.chap2; public class Demo5 { public static void main(String[] args) throws InterruptedException { Demo05User user = new Demo05User(); Thread t = new Demo05Thread(user); t.start(); Thread.sleep(200); user.getValue(); } } class Demo05User{ private String username = "a"; private String password = "aa"; synchronized public void setUsernameAndPassword(String username, String password){ this.username = username; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.password = password; System.out.println("setUsernameAndPassword method, thread name: "+ thread.currentThread ().getName() + ", username=" +username + ", password=" + password"); } synchronized public void getValue(){system.out.println ("getValue "); Thread name "+ thread.currentThread ().getName() + ", username=" + username + ", password=" + password); } } class Demo05Thread extends Thread{ private Demo05User user; public Demo05Thread(Demo05User user){ this.user = user; } @Override public void run() { user.setUsernameAndPassword("B", "BB"); }} Copy the codeCopy the code

Results:

package com.jiangxia.chap2; public class Demo5 { public static void main(String[] args) throws InterruptedException { Demo05User user = new Demo05User(); Thread t = new Demo05Thread(user); t.start(); Thread.sleep(200); user.getValue(); } } class Demo05User{ private String username = "a"; private String password = "aa"; synchronized public void setUsernameAndPassword(String username, String password){ this.username = username; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.password = password; System.out.println("setUsernameAndPassword method, thread name: "+ thread.currentThread ().getName() + ", username=" +username + ", password=" + password"); } public void getValue(){system.out.println ("getValue "); Thread name "+ thread.currentThread ().getName() + ", username=" + username + ", password=" + password); } } class Demo05Thread extends Thread{ private Demo05User user; public Demo05Thread(Demo05User user){ this.user = user; } @Override public void run() { user.setUsernameAndPassword("B", "BB"); }} Copy the codeCopy the code

Results:

When thread A calls A method that uses the Synchronize keyword, thread A acquires A method lock, or rather an object lock, so other threads must wait for thread A to complete their execution before calling other synchronized methods. At this point, thread A has completed A complete task, that is, username and password have been assigned at the same time, so there is no dirty read in this case.

6. Lock reentrant

Reentrant lock: You can acquire your own internal lock again. For example, thread A has acquired A lock on an object, but the lock has not been released yet. When thread A wants to acquire the lock again, it can still acquire the lock. If the lock is not reentrant, A deadlock will occur. Reentrant locks are also supported in parent-child inheritance environments.

The keyword Synchronized has the reentrant function of the lock. That is, when a thread obtains an object lock with Synchronized, it can obtain the object lock again when it requests it. Such as:

package com.jiangxia.chap2; public class Demo6 { public static void main(String[] args) { Thread t = new Demo06Thread(); t.start(); }} class Demo06Service{synchronized public void foo1(){system.out.println ("foo1 "); foo2(); Synchronized public void foo2(){system.out.println ("foo2 "); synchronized public void foo2(){system.out.println ("foo2 "); foo3(); Synchronized public void foo3(){system.out.println ("foo3 "); synchronized public void foo3(){system.out.println ("foo3 "); } } class Demo06Thread extends Thread{ @Override public void run() { Demo06Service service = new Demo06Service(); service.foo1(); }} Copy the codeCopy the code

Results:

Reentrant locks are also supported in parent-child inheritance environments. Such as:

public class Demo6 { public static void main(String[] args) { Thread t = new Demo06Thread(); t.start(); }} class Demo06Service{synchronized public void foo1(){system.out.println ("foo1 "); foo2(); Synchronized public void foo2(){system.out.println ("foo2 "); synchronized public void foo2(){system.out.println ("foo2 "); foo3(); Synchronized public void foo3(){system.out.println ("foo3 "); synchronized public void foo3(){system.out.println ("foo3 "); } } class Demo06Thread extends Thread{ @Override public void run() { // Demo06Service service = new Demo06Service(); // service.foo1(); // Subclass Demo06ServiceB = new Demo06ServiceB(); serviceB.foo4(); }} class Demo06Service extends Demo06Service{synchronized public void foo4(){system.out.println ("foo4 method "); super.foo1(); }} Copy the codeCopy the code

Results:

7. Automatic release of locks

When code executed by a thread fails, the lock it holds should be released automatically, for example:

public class Demo7 { public static void main(String[] args) { DemoService7 service7 = new DemoService7(); Thread t1 = new DemoThread7(service7); t1.setName("A"); t1.start(); Thread t2 = new DemoThread7(service7); t2.start(); } } class DemoService7{ synchronized public void foo(){ if("A".equals(Thread.currentThread().getName())){ System.out.println(" thread A starts with "+ system.currentTimemillis ()); While (true){if((""+ math.random ()).substring(0,8).equals("0.123456")){system.out.println (" thread A ends at: "+System.currentTimeMillis()); Integer.parseInt("A"); }}}else{system.out.println (" thread B starts with "+ system.currentTimemillis ()); } } } class DemoThread7 extends Thread{ private DemoService7 service7; public DemoThread7(DemoService7 service7){ this.service7 = service7; } @Override public void run() { service7.foo(); }} Copy the codeCopy the code

Results:

Synchronization does not have inheritance

Synchronized is not inherited, as in:

public class Demo8 { public static void main(String[] args) { DemoService8B serviceB = new DemoService8B(); Thread t1 = new Demo8Thread(serviceB); t1.setName("A"); t1.start(); Thread t2 = new Demo8Thread(serviceB); t2.setName("B"); t2.start(); }} class DemoService8A{synchronized public void foo(){try{system.out.println (" "+ thread.currentThread ().getName() +" + system.currentTimemillis ()); Thread.sleep(5000); Println (" parent: "+ thread.currentThread ().getName() +" + system.currentTimemillis ()); }catch(InterruptedException e){ e.printStackTrace(); }} class DemoService8B extends DemoService8A{public void foo(){try {system.out.println (" "+ thread.currentThread ().getName() +" + system.currentTimemillis ()); Thread.sleep(5000); Println (" subclass: "+ thread.currentThread ().getName() +" + system.currentTimemillis ()); super.foo(); }catch (InterruptedException e){ e.printStackTrace(); } } } class Demo8Thread extends Thread{ DemoService8B service; public Demo8Thread(DemoService8B service){ this.service = service; } @Override public void run() { service.foo(); }} Copy the codeCopy the code

Results:

3 summary

The above is some content of synchronized keyword modification in Java multithreaded programming. Synchronized keyword can modify not only methods, but also code blocks. A future article will cover the drawbacks of synchronized keyword modification and how it modifies blocks of code.

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

Related recommendations:

  • Use of thread pools for multithreaded programming

  • How to suspend and resume threads in multithreaded programming

  • Several methods of stopping threads in multithreaded programming

  • Common apis for multithreaded programming threads

  • Threads and common methods for threads