Concurrent programming - immutable object of thread safety policy

Posted by philicious on Wed, 05 Jan 2022 00:40:19 +0100

Brain map

Four thread safety policies

 

Thread limit

An object restricted by a thread, which is exclusive to the thread and can only be modified by the thread that owns it

shared read-only

A shared read-only object can be accessed concurrently by multiple threads without additional synchronization, but no thread can modify it

Thread safe object

A thread safe object or container is internally thread safe through the synchronization mechanism, so other threads can access it freely through the public interface without additional synchronization

Guarded object

Guarded objects can only be accessed by acquiring specific locks

Immutable object definition

In Java, there is an object that is safe when published, which is called immutable object.

Immutable objects can be used in multithreading to ensure thread safety

Conditions to be met by immutable objects

  • After an object is created, its state cannot be modified
  • All fields of the object are of type final
  • The object was created correctly (this reference did not escape during object creation)

How to create immutable objects

 

  • Declare the class as final so that it cannot be inherited
  • Make all members private so that other classes and objects cannot directly access these members
  • No set method is provided for variables
  • Declare all mutable members as final so that they can only be assigned once
  • Initialize all members through the constructor for deep copy
  • In the get method, instead of directly returning the object itself, the object is cloned and a copy of the object is returned

When it comes to immutable objects, we have to mention the final keyword, which can modify classes, methods and variables:

Use the final keyword to define immutable objects

final keyword can modify classes, methods and variables

  • Decorated class: cannot be inherited (all methods in the final class will be implicitly declared as final methods)
  • Modification method:

1. The locking method is not modified by the inherited class;
2. Improve efficiency (private method is implicitly modified as final method)

  • Modifier variable:

Basic data type variable: cannot be modified after initialization
Reference type variable: its reference cannot be modified after initialization

  • Modification method parameters: the same as modification variables

Modified variable example

final modifies the basic data type and String: it cannot be modified after initialization (thread safety)

It can be seen that the compilation reports an error. After being modified by final, the basic type and String variables cannot be modified

final modified reference type variable: its reference cannot be modified after initialization, but its value can be modified (thread unsafe)

package com.artisan.example.immutable;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FinalDemo {

	// Basic data type int uses final modification to verify that the basic data type modified by final cannot be changed
	private final static int num = 1;

	// String type uses final modification to verify that the basic data type modified by final cannot be changed
	private final static String name = "Small craftsman";

	// After a reference type is initialized, its reference cannot be modified, but its value can be modified
	private final static Map<String, Object> map = Maps.newHashMap();

	static {
		map.put("name", "artisan");
		map.put("age", 20);
		map.put("sex", "male");
	}

	public static void main(String[] args) {

		// The basic data type and String modified by final cannot be changed
		// Compilation error: the final field finaldemo num cannot be assigned
		// num = 2;
		// Compilation error: the final field finaldemo name cannot be assigned
		// name = "artisan";

		// Reference object. This reference cannot point to another object, but the value of the object can be modified
		map.put("name", "Small craftsman");
		log.info("name:{}", map.get("name"));
		
		
		// Verify that the method parameters are modified by final
		List<String> list = new ArrayList<>();
		list.add("I'm a craftsman");
		test2(list);
		
	}

	// The basic type of the variable passed in by the final modifier cannot be changed
	private void test(final int a) {
		// Cannot modify
		// The final local variable a cannot be assigned It must be blank and not using a compound assignment
		// a = 2;
		log.info("a:{}", a);
	}

	// The variable passed in by the final modification method refers to an object. It cannot point to other objects, but the value of the object can be modified
	private static void test2(final List<String> list) {
		// Add data
		list.add("I am artisan");
		list.forEach(str ->{
			log.info("data:{}",str);
		});
	}

}

It should be noted that when final modifies the reference type, although the reference cannot be pointed to another object, the value of the object can be modified. Thread unsafe

Use the tool classes provided in JDK / Guava to create immutable objects

In addition to final, immutable objects can be defined. The Collections class provided by java can also define immutable objects.

  • Collection. In JDK The object passed in by unmodifiablexxx cannot be modified once initialized. XXX can represent collection, List, Set, Map, etc

  • ImmutableXXX in Google's Guava can also represent Collection, List, Set, Map, etc

Collections.unmodifiableXXX example (thread safe)

The results after implementation are as follows:

 

Thus, use collections The object modified by unmodifiablemap cannot be modified. If you try to modify the value of the object, an exception will be thrown when the program runs.

Follow collections Unmodifiablemap source code

 

Continue to look at UnmodifiableMap

 

It mainly changes all update methods of a new collection into throwing exceptions

Guava ImmutableXXX example (thread safe)

package com.artisan.example.immutable;

import com.artisan.anno.ThreadSafe;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;


@ThreadSafe
public class GuavaImmutableSetDemo {

	// Use the classes provided in Guava to define a collection of immutable objects
	
	// Immutable list
	private final static ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);
	// Immutable set
	private final static ImmutableSet<Integer> set = ImmutableSet.copyOf(list);
	// For an immutable map, data needs to be passed in the form of k/v, that is, the odd bit parameter is key and the even bit parameter is value
	private final static ImmutableMap<String, String> map = ImmutableMap.of("k1", "v1", "k2","v2");
	// The immutable map is constructed through the builder call chain
	private final static ImmutableMap<String, String> map2 = ImmutableMap.<String, String>builder()
																			.put("key1", "value1")
																			.put("key2", "value2")
																			.put("key3", "value3")
																			.build();

	public static void main(String[] args) {
		
		// If you modify the data in the object, an unsupported operationexception will be thrown
		
		// Unable to add new elements, the runtime will throw Java lang.UnsupportedOperationException
		list.add(4);

		// Unable to add new elements, the runtime will throw Java lang.UnsupportedOperationException
		set.add(4);

		// Unable to add new elements, the runtime will throw Java lang.UnsupportedOperationException
		map.put("k3", "v3");

		// Unable to add new elements, the runtime will throw Java lang.UnsupportedOperationException
		map2.put("key4", "value4");
	}
}


 

The above code is thread safe. During development, if our object can be changed into an immutable object, we try to change the object into an immutable object, so as to avoid thread safety problems.  

Topics: Multithreading