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