java multithreaded summary-synchronized keyword for synchronization

Posted by DrDre on Thu, 08 Aug 2019 12:31:27 +0200

java multithreaded summary-synchronized keyword for synchronization

1. Why use synchronized?

Thread security problems exist in concurrent programming for the following reasons:
1. Shared data exists
2. Multi-threads work together to share data. Keyword synchronized ensures that at the same time, only one thread can execute a method or a block of code, while synchronized ensures that a thread's changes are visible.

2. What is synchronized lock and what is the purpose of locking?

synchronized locks are objects, which may be this, critical resource objects, class objects
The purpose of locking is to ensure the atomicity of the operation.

3. Code examples

3.1 Lock this and Critical Resource Objects

In this case:

  • TesSync1 is a synchronized block of code that locks objects, critical resource objects
  • TesSync2 and testSync3 are both locks on the current object this
/**
 * synchronized Keyword
 * Lock objects. Both synchronized(this) and synchronized methods lock the current object.
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_01 {
	private int count = 0;
	private Object object = new Object();

	// 1.synchronized code block, which locks object, critical resource object
	public void testSync1(){
		synchronized(object){
			System.out.println(Thread.currentThread().getName() 
					+ " count = " + count++);
		}
	}

	// 2.synchronized code block, which locks the current object this
	public void testSync2(){
		synchronized(this){
			System.out.println(Thread.currentThread().getName() 
					+ " count = " + count++);
		}
	}

	// 2.synchronized code, the lock is also this
 	public synchronized void testSync3(){
		System.out.println(Thread.currentThread().getName() 
				+ " count = " + count++);
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		final Test_01 t = new Test_01();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.testSync3();
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.testSync3();
			}
		}).start();
	}
	
}

3.2 Lock class object

/**
 * synchronized Keyword
 * Synchronization method - static
 * The static synchronization method locks the class object of the current type. In this code, it's Test_02.class.
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_02 {
	private static int staticCount = 0;
	
	public static synchronized void testSync4(){
		System.out.println(Thread.currentThread().getName() 
				+ " staticCount = " + staticCount++);
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void testSync5(){
		synchronized(Test_02.class){
			System.out.println(Thread.currentThread().getName() 
					+ " staticCount = " + staticCount++);
		}
	}
	
}

3.3 When to lock critical resources and when to lock current objects?

If access restriction is required for the current object when locking, it is recommended to lock critical resources (that is, to lock a critical resource) and lock the current object if the level of the current lock is relatively high. Suggestions are made to develop in a synchronous code block manner, so that the scope of locks is not too high.

Synchronization method and asynchronization method
Question: Does synchronization affect the synchronization method that other threads call asynchronous methods or other lock resources?

Code examples
M1 is an asynchronous method, M2 is a synchronous method, m3 is a block of synchronous code, the critical resource of lock. The purpose of this code is to prove whether m2 and M3 can execute when calling the synchronous method m1.

/**
 * synchronized Keyword
 * Synchronized Method - Call of Synchronized Method and Asynchronized Method
 * The synchronization method only affects the synchronization method of locking the same lock object. It does not affect other threads to call asynchronous methods or to call synchronous methods for other lock resources.
 */
package com.bernardlowe.concurrent.t01;

public class Test_04 {
	Object o = new Object();
	public synchronized void m1(){ // Heavy access operations.
		System.out.println("public synchronized void m1() start");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("public synchronized void m1() end");
	}
	
	public void m3(){
		synchronized(o){
			System.out.println("public void m3() start");
			try {
				Thread.sleep(1500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("public void m3() end");
		}
	}
	
	public void m2(){
		System.out.println("public void m2() start");
		try {
			Thread.sleep(1500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("public void m2() end");
	}
	
	public static class MyThread01 implements Runnable{
		public MyThread01(int i, Test_04 t){
			this.i = i;
			this.t = t;
		}
		int i ;
		Test_04 t;
		public void run(){
			if(i == 0){
				t.m1();
			}else if (i > 0){
				t.m2();
			}else {
				t.m3();
			}
		}
	}
	
	public static void main(String[] args) {
		Test_04 t = new Test_04();
		new Thread(new MyThread01(0, t)).start();
		new Thread(new MyThread01(1, t)).start();
		new Thread(new MyThread01(-1, t)).start();
	}
	
}

results of enforcement

It can be seen that when the synchronization method m1 is executed, m2 and m3 are not affected and can still be executed.
Conclusion: Synchronization method only affects the synchronization method of locking the same lock object. It does not affect other threads to call asynchronous methods or to call synchronous methods for other lock resources.
However, it is important to avoid synchronization in commercial development. Use synchronous code blocks. Fine-grained solution to synchronization problems.

3.3 Re-entry Lock

There are two types of re-entry locks:

  • 1. Call other synchronization methods in synchronization methods
  • 2. Subclass synchronization overrides parent synchronization

Let's look at the first one: calling other synchronization methods in a synchronization method
Think: If you call the m1() method, will the m2() method be executed?

/**
 *synchronized Keyword
 *Synchronization Method - Call Other Synchronization Methods
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_06 {
	
	synchronized void m1(){ // Lock this
		System.out.println("m1 start");
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		m2();
		System.out.println("m1 end");
	}
	synchronized void m2(){ // Lock this
		System.out.println("m2 start");
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("m2 end");
	}
	
	public static void main(String[] args) {
		
		new Test_06().m1();
		
	}
	
}  


Result: When the m1() method is called, the m2() method is still executable. Multiple calls to synchronization code to lock the same lock object are reentrant

The second case: the subclass synchronization method overrides the parent synchronization method
Think: In the sub-class synchronization method m(), if the parent synchronization method m() is called, is it reentrant?

/**
 * synchronized Keyword
 * Synchronization method - inheritance
 * The child class synchronization method overrides the parent class synchronization method. You can specify the synchronization method that calls the parent class.
 * Equivalent to lock reentry.
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_07 {
	
	synchronized void m(){
		System.out.println("Super Class m start");
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Super Class m end");
	}
	
	public static void main(String[] args) {
		new Sub_Test_07().m();
	}
	
}

class Sub_Test_07 extends Test_07{
	synchronized void m(){
		System.out.println("Sub Class m start");
		super.m();
		System.out.println("Sub Class m end");
	}
}


Result: In the sub-class synchronization method m(), the parent synchronization method m() is called, which can be reentered.

3.4 Locks and Abnormalities

Think: When an exception occurs in a synchronization method or block of synchronization code, will it affect the execution of other threads?
Let's look at a piece of code

/**
 * synchronized Keyword
 * Synchronization method - lock and exception
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_08 {
	int i = 0;
	synchronized void m(){
		System.out.println(Thread.currentThread().getName() + " - start");
		while(true){
			i++;
			System.out.println(Thread.currentThread().getName() + " - " + i);
			try {
				TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// An exception is thrown when i==5
			if(i == 5){
				i = 1/0;
			}
		}
	}
	
	public static void main(String[] args) {
		final Test_08 t = new Test_08();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		}, "t1").start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		}, "t2").start();
	}
	
}

This code first runs two threads t1 and t2, and if one thread has an exception, can the other thread continue to execute?

Result: When an exception occurs in the synchronization method, the lock resource is automatically released. It does not affect the execution of other threads.

Think: How to deal with exceptions in synchronous business logic?
For example, in the code above which exceptions occur, you can do this

			if(i == 5){
				try {
					i = 1/0;
				} catch (Exception e) {
					i = 0;
				}
			}

3.5 Lock Object Change Problem

Code examples: 8
Think: When one thread executes a synchronization method, another thread modifies the lock object and whether it can execute a synchronization block of code.

/**
 * synchronized Keyword
 * Lock Object Change Problem
 */
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_13 {
	Object o = new Object();
	
	void m(){
		System.out.println(Thread.currentThread().getName() + " start");
		synchronized (o) {
			while(true){
				try {
					TimeUnit.SECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + " - " + o);
			}
		}
	}
	
	public static void main(String[] args) {
		final Test_13 t = new Test_13();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		}, "thread1").start();
		try {
			TimeUnit.SECONDS.sleep(3);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		Thread thread2 = new Thread(new Runnable() {
			@Override
			public void run() {
				t.m();
			}
		}, "thread2");
		t.o = new Object();
		thread2.start();
	}
	
}


Conclusion: It can be seen that other threads can still execute synchronization methods.
Because once the synchronization code is locked, there will be a temporary lock reference to execute the lock object, which is not directly related to the real reference. Modifying lock object references will not affect the execution of synchronous code until the lock is released.

Note: Do not use static constants as lock objects
The following code, because the String constant pool problem, s1,s1 is the same object, so m1,m2 method lock is the same object, M1 synchronization method is executed, m2 method will not be executed.

/**
 * synchronized Keyword
 * Constant problem
 */
package com.bernardlowe.concurrent.t01;

public class Test_14 {
	String s1 = "hello";
	String s2 = "hello";
	// String S2 = new String ("hello"); the // new keyword must create a new object in the heap.
	void m1(){
		synchronized (s1) {
			System.out.println("m1()");
			while(true){
				
			}
		}
	}
	
	void m2(){
		synchronized (s2) {
			System.out.println("m2()");
			while(true){
				
			}
		}
	}
	
	public static void main(String[] args) {
		final Test_14 t = new Test_14();
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m1();
			}
		}).start();
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				t.m2();
			}
		}).start();
	}
	
}

Topics: Java Programming