Programmer: Concurrent how to keep shared variables secure and unlocked?!

Keywords: Java encoding Database socket

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.

Posted by dvayne on Wed, 25 Sep 2019 19:16:00 -0700