Immutable objects and protective copies of shared variables!

1. Preface

Main contents:

  • Use of immutable classes

  • Immutable class design

  • Stateless class design

2. Date conversion

reference

  • https://blog.csdn.net/csdn_ds/article/details/72984646

SimpleDateFormat problem

Under multithreading, the SimpleDateFormat will have a concurrency problem (NumberFormatException)

package cn.knightzz.unsafe;

import lombok.extern.slf4j.Slf4j;

import java.text.ParseException;
import java.text.SimpleDateFormat;

@SuppressWarnings("all")
@Slf4j(topic = "c.SimpleDateFormatTest")
public class SimpleDateFormatTest {

    public static void main(String[] args) {

        // Multiple threads resolve characters by using
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                try {
                    log.debug("{}" , sdf.parse("1921-04-21"));
                } catch (ParseException e) {
                    log.debug("{}" , e);
                }
            }, "t" + i).start();
        }
        
    }
}

Source code analysis of SimpleDateFormat

The reason is simple:

The underlying layer of SimpleDateFormat uses a Calendar object to store date information. In a concurrent environment, multiple threads will share the same Calendar object

In the parsing method parseddate = CALB. Established (calendar). Gettime();

It can be seen from the above that when the string is parsed even, it is clear first and then set. Under multithreading, the problem of coverage will certainly occur. For example, the t1 thread has just executed set() and then the thread context is switched. The t2 thread directly gives clear() and then switches to the t1 thread. At this time, the result is empty

Solution - synchronous lock

  // Multiple threads resolve characters by using
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
               synchronized (sdf){
                   try {
                       log.debug("{}" , sdf.parse("1921-04-21"));
                   } catch (ParseException e) {
                       log.debug("{}" , e);
                   }
               }
            }, "t" + i).start();
        }

Although this can solve the problem, it brings performance loss

Solution - DateTimeFormatter

If an object cannot modify its internal state (property), it is thread safe because there is no concurrent modification! Such objects are

There are many in Java. For example, after Java 8, a new date format class is provided: DateTimeFormatter

 // Multiple threads resolve characters by using
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                LocalDate date = dtf.parse("2020-02-23" , LocalDate::from);
                log.debug("==> {}" , date);
            }, "t" + i).start();
        }

3. Immutable design

Design an immutable class

public final class String
 implements java.io.Serializable, Comparable<String>, CharSequence {
 /** The value is used for character storage. */
 private final char value[];
 /** Cache the hash code for the string */
 private int hash; // Default to 0
 
 // ...
 
}
  • All privatized properties do not provide a set method
  • It is found that the class and all properties in the class are fifinal
  • The attribute is decorated with final to ensure that the attribute is read-only and cannot be modified
  • The class is decorated with final to ensure that the methods in the class cannot be overwritten and prevent the subclass from inadvertently destroying the immutability

4. Protective copy

Definition: avoid variable sharing by copying replica objects

public String substring(int beginIndex) {
 if (beginIndex < 0) {
 	throw new StringIndexOutOfBoundsException(beginIndex);
 }
 int subLen = value.length - beginIndex;
 if (subLen < 0) {
 	throw new StringIndexOutOfBoundsException(subLen);
 }
 	return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

Take substring as an example. It performs security verification first, and then creates a new String object instead of modifying the original stir. This can prevent concurrent security problems caused by multiple variable sharing

The inner part is to call the String construction method to create a new String, and then enter the construction to see whether to make a response to the fifinal char[] value

Modified:

 if (offset < 0) {
 	throw new StringIndexOutOfBoundsException(offset);
 }
 if (count <= 0) {
 	if (count < 0) {
 	throw new StringIndexOutOfBoundsException(count);
 	}
 if (offset <= value.length) {
 	this.value = "".value;
 	return;
 	}
 }
 if (offset > value.length - count) {
 	throw new StringIndexOutOfBoundsException(offset + count);
 }
 	this.value = Arrays.copyOfRange(value, offset, offset+count);
}

When constructing a new string object, a new char[] value will be generated to copy the content. This is avoided by creating duplicate objects

The sharing free means is called protective copy

Tags: Java Interview jvm

Posted by digitalflash on Fri, 19 Aug 2022 15:39:07 +0530