This blog Uncle Cat's Blog , reprint please state source
Read this for about "15 minutes"
Readable Population: Java Intermediate
Learn notes, take a two-day break (actually doing a simulation project in real life), occasionally think about what you should do on earth, is it really meaningful to yourself or society?
Photo on Visual hunt
Say your answer
emmm, there's more than one answer. Today, let's start with a simple and easy-to-understand one
Read the topic: How should we ensure thread security for shared variable access while avoiding the overhead of introducing locks?
In a concurrent environment, where an object is easily shared by multiple threads, data consistency is required
Although explicit locks or CAS operations can be used, this also incurs some additional overhead such as context switching
Let's start with an example of the current problem
/** * @ClassName Cup * @Description Non-threaded cup safety * @Author MySelf * @Date 2019/9/25 21:28 * @Version 1.0 **/ public class Cup { //diameter private double diameter; //height private double height; public double getDiameter() { return diameter; } public double getHeight() { return height; } //Nonatomic operation public void setCup(double diameter,double height){ this.diameter = diameter; this.height = height; } }
You should be able to see that this code is non-thread safe, right (if you can't see it, review it in the last article)
Because when we assign a diameter to a setCup operation, it is possible that another thread has already started reading its height, which can cause thread security problems.
So what can I do without a lock?
Look down, it's almost as long as brushing your circle of friends. You'll soon understand.
Immutable object
Yes, the way we say today is that Cup becomes an immutable object!
Immutable Objects: Once an object is created, its visible state remains unchanged (like String, Integer)
So how do the updates above need to be modified?
/** * @ClassName Cup * @Description Immutable Objects, Thread Safe * @Author MySelf * @Date 2019/9/25 21:32 * @Version 1.0 **/ public final class Cup { private final double diameter; private final double height; public Cup(double diameter,double height){ this.diameter = diameter; this.height = height; } }
That's fine. It'll never change
Wait, how do I modify Cup?Even with concurrent operations, my business may need to modify this Cup
Let's adjust our horizon and modify the Cup property==to replace the Cup instance
Suppose we are a tea cup casting factory with 5 pipelines generating the nearest net black tea cup. However, due to the impression of Internet trends, we occasionally need to make small changes to this tea cup parameter. Out-of-service production will lose money, so on the code of the mould adapter we can replace it with an immutable object.Teacup Properties
/** * @ClassName MoldAdapter * @Description Mould adapter * @Author MySelf * @Date 2019/9/25 21:35 * @Version 1.0 **/ public class MoldAdapter { private Map<String,Cup> cupMap = new ConcurrentHashMap<String, Cup>(); public void updateCup(String version,Cup newCup){ cupMap.put(version, newCup); } }
The locks involved inside ConcurrentHashMap here have nothing to do with the creation or replacement of teacups in Demo, and the process does not involve locks
Maybe a little vague, talk about doll machine cases?
Remember the doll machine that destroyed my youth?
I remember taking her to play with the doll maker a long time ago when she was still picking up her wife. I boasted that I could catch the one she wanted. The result was...
It's called 50
Now it's our turn to turn over and be the master, hem
Suppose we're the head of a block doll machine. Each doll machine has its corresponding machine number, two-dimensional code url, and manipulator frequency (for nonprofessional mechanics, suppose this is the key to making money). Suppose we're a money-hungry underpants youth, cleared every nightOne-time income list.
Recently, during the National Day, the number of tourists is going up...
Insert, hereby the public number wishes the 70th anniversary of the motherland prosperous and prosperous, and the country peace!
Want to make money, search has been jumping to the chest
That's a good way to get the frequency of the manipulators on the doll machine
I need to modify some of the doll machine properties specifically, but fortunately I started with a mapping table encoding the relationship with the doll machine
I brought the idea of immutable objects into my profitable business
First the doll machine object, first the immutable object
/** * @ClassName DollMachineInfo * @Description Doll maker immutable object * @Author MySelf * @Date 2019/9/25 21:51 * @Version 1.0 **/ public final class DollMachineInfo { //number private final String number; //Pay QR url private final String url; //Manipulator Frequency private final int frequency; public DollMachineInfo(String number,String url,int frequency){ this.number = number; this.url = url; this.frequency = frequency; } public DollMachineInfo(DollMachineInfo dollMachineInfoType){ this.number = dollMachineInfoType.number; this.url = dollMachineInfoType.url; this.frequency = dollMachineInfoType.frequency; } public String getNumber() { return number; } public String getUrl() { return url; } public int getFrequency() { return frequency; } }
This time I need to modify the relational mapping table that codes to the doll machine, so this table needs to be immutable, he needs to support me in getting the relational mapping table, and he needs to replace the latest relational mapping content
/** * @ClassName MachineRouter * @Description Machine Information Table * @Author MySelf * @Date 2019/9/25 21:57 * @Version 1.0 **/ public final class MachineRouter { //Ensure memory visibility in concurrent environments private static volatile MachineRouter instance = new MachineRouter(); //Mapping relationship between code and machine private final Map<String,DollMachineInfo> routeMap; // 2. Store non-variable routeMap public MachineRouter(){ //Load data from database tables into memory and save as Map this.routeMap = MachineRouter.setRouteFromeDB(); } // 3. Store data in Map from db private static Map<String, DollMachineInfo> setRouteFromeDB(){ Map<String, DollMachineInfo> map = new HashMap<String, DollMachineInfo>(); //DB Code return map; } // 1. Initialize instances public static MachineRouter getInstance(){ return instance; } /** * Get corresponding machine information from code * @param code Correspondence encoding * @return Machine Information */ public DollMachineInfo getMacheine(String code){ return routeMap.get(code); } /** * Modify the current MachineRouter instance * @param newInstance New instance */ public static void setInstance(MachineRouter newInstance){ instance = newInstance; } private static Map<String, DollMachineInfo> deepCopy(Map<String,DollMachineInfo> d){ Map<String, DollMachineInfo> result = new HashMap<String, DollMachineInfo>(); for (String key : d.keySet()){ result.put(key, new DollMachineInfo(d.get(key))); } return result; } public Map<String, DollMachineInfo> getRouteMap() { //defensive copying return Collections.unmodifiableMap(deepCopy(routeMap)); } }
The next step is to add the update code to your concurrent business
/** * @ClassName Worker * @Description Communication docking class * @Author MySelf * @Date 2019/9/25 22:13 * @Version 1.0 **/ public class Worker extends Thread { @Override public void run(){ boolean isRouterModification = false; String updateMachineInfo = null; while (true){ //Remaining business code /** * Parse in the communication Socket information, update the data table information, and reset the MachineRouter instance */ if (isRouterModification){ if ("DollMachineInfo".equals(updateMachineInfo)){ MachineRouter.setInstance(new MachineRouter()); } } //Remaining business code } } }
Knock on the blackboard and take notes
At the end of the case, there is a basic concept, so let's talk about technical terms
This is an immutable object pattern, Immutable Object
Strictly speaking, what conditions do immutable objects need to satisfy:
- 1. Class itself has fianl: prevent behavior defined by subclasses from being modified
- 2. fianl modifies all fields: JMM can be used under multi-threading to ensure the security of initialization of the object referenced by the modified field
- 3. The this keyword was not given to other classes when the object was created
- 4. If other objects (arrays, collections) with variable state are referenced, they must be private and cannot be exposed to the outside world. If fields need to be returned, defensive copies should be made.
There are two important things about the Immutable Object pattern that you should know about
ImmutableObject: Responsible for storing a set of immutable States
- getState*: Returns the value of the relevant variable maintained by ImmutableObject, which is instantiated with the constructor's parameters
- getStateSnapshot: Returns a set of state snapshots maintained by ImmutableObject
Manipulator: Maintains changes to ImmutableObject and participates in generating new ImmutableObject instances when changes are required
- changeStateTo: Generate a new ImmutableObject instance based on a new status value
Typical interactive scenarios
- 1. Get the status values of the current ImmutableObject
- 2. Call the changeStateTo method of Manipulator to update the application status
- 3. changeStateTo Create a new instance of ImmutableObject to reflect the new state and return
- 4. Get a status snapshot of the new ImmutableObject
What scenarios are suitable for use
Yes, he does meet our title requirements, but any design pattern has its own scenario
Generally more suitable:
- Less frequently changed objects (doll machine case)
- Write data sets to ensure atomicity (Teacup case)
- Use an object as the key to HashMap (note the HashCode of the object)
Notes:
- Frequent object changes: CPU consumption and GC burden
- Defensive replication: Avoid external code modifying its internal state
Professional Cases
Collection traversal is often introduced in a multithreaded environment to prevent changes in the internal structure of the collection during traversal
ImmutableObject mode is used in java.util.concurrent.CopyOnWriteArrayList
Scenes are also needed, of course, in scenes that are traversed more frequently than modifications
An array variable is maintained internally to store collections. When you add an element, it generates a new array, copies the collection elements to the new array, and sets the last element to the added element, and copies the new array to the array.
That is, array references an array that can be equivalent to an ImmutableObject, note that it is
Therefore, when traversing CopyOnWriteArrayList, an Iterator instance is generated directly from the array instance without locking
epilogue
Because the last CopyOnWriteArrayList didn't take the source code seriously, I won't go into detail. It's mainly because you can understand the immutable object model. It's best to write a Demo. I hope you can use this idea in production environment. Your writing is awkward and forgiving.
I'm MySelf, and I'm still learning about technology and product managers. I hope this article will bring you new points of knowledge.
Public number: Java cat says
Learning and Communication Group: 728698035
Current architecture design (code farmer) and entrepreneurship technical consultant, unrestrained mediocrity, love open source, chat about program life and irregular dry goods.