1. Design of Invariant Classes
Invariant: An object is an immutable object if it cannot modify its internal state (properties)
Thread-safe, concurrent modification and visibility issues for immutable objects are another way to avoid competition
String classes are also immutable, and all attributes within the class are final
-
The final modification of the class guarantees that the methods in the class cannot be overridden, preventing subclasses from unintentionally destroying immutability
-
No write method (set) ensures that internal properties cannot be modified externally
-
Property modification with final ensures that it is read-only and cannot be modified
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; //.... }
-
When you change the String class data, a new string object is constructed, a new char[] value is generated, and a copy object is created to avoid sharing in a way called a protective copy
2. final
principle
public class TestFinal { final int a = 20; }
Byte code:
0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: bipush 20 // Place values directly on the stack 7: putfield #2 // Field a:I <-- Write barrier 10: return
The final variable is assigned through the putfield directive, after which a write barrier is added to ensure that no zero occurs when other threads read its value
Other threads accessing final-decorated variables make a copy and put it on the stack, which is more efficient
3. Hedonic Design Mode
- The introduction defines the English name Flyweight pattern, which reuses a limited number of objects of the same type.
- Structural pattern
The enjoyment mode reflects:
1. Packaging classes such as Boolean, Byte, Short, Integer, Long, Character etc. in JDK provide valueOf methods. For example, Long's valueOf caches Long objects between -128 and 127. Objects are reused between these ranges, beyond which new Long objects are created.
public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); }
- The range of Byte, Short, Long caches is -128-127
- Character cache range is 0-127
- Boolean caches TRUE and FALSE
- Integer has a default range of -128~127, and the minimum cannot be changed, but the maximum can be changed by adjusting the virtual machine parameter'-Djava.lang.Integer.IntegerCache.high'
2. String String Pool
3,BigDecimal, BigInteger
4. Implement a simple connection pool
For example: an online store application with thousands of QPS will have a significant performance impact if database connections are re-created and closed each time. A batch of connections are pre-created and put into the connection pool. Once a request arrives, the connection is acquired from the connection pool and returned to the connection pool after it has been used. This saves connection creation and closure time and enables connection reuse without overwhelming the database with a large number of connections.
/** * Description: Simple Connection Pool * * @author guizy * @date 2020/12/29 21:21 */ public class Test2 { public static void main(String[] args) { /*Use connection pool*/ Pool pool = new Pool(2); for (int i = 0; i < 5; i++) { new Thread(() -> { Connection conn = pool.borrow(); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } pool.free(conn); }).start(); } } } @Slf4j(topic = "guizy.Pool") class Pool { // 1. Connection pool size private final int poolSize; // 2. Array of Connected Objects private Connection[] connections; // 3. Connection state array: 0 for idle, 1 for busy private AtomicIntegerArray states; // 4. Initialization of construction methods public Pool(int poolSize) { this.poolSize = poolSize; this.connections = new Connection[poolSize]; this.states = new AtomicIntegerArray(new int[poolSize]);//Use AtomicIntegerArray to keep states thread safe for (int i = 0; i < poolSize; i++) { connections[i] = new MockConnection("Connect" + (i + 1)); } } // 5. Borrow Connections public Connection borrow() { while (true) { for (int i = 0; i < poolSize; i++) { // Get idle connections if (states.get(i) == 0) { if (states.compareAndSet(i, 0, 1)) {//Use compareAndSet to secure threads log.debug("borrow {}", connections[i]); return connections[i]; } } } // If there is no idle connection, the current thread enters the wait, and if this synchronized is not written, other threads will not wait. // Always above while(true), idle, consuming cpu resources synchronized (this) { try { log.debug("wait..."); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 6. Return Connections public void free(Connection conn) { for (int i = 0; i < poolSize; i++) { if (connections[i] == conn) { states.set(i, 0); synchronized (this) { log.debug("free {}", conn); this.notifyAll(); } break; } } } } class MockConnection implements Connection { private String name; public MockConnection(String name) { this.name = name; } @Override public String toString() { return "MockConnection{" + "name='" + name + '\'' + '}'; } // Connection implementation slightly }
22:01:07.000 guizy.Pool [Thread-2] - wait... 22:01:07.000 guizy.Pool [Thread-0] - borrow MockConnection{name='Connection 1'} 22:01:07.005 guizy.Pool [Thread-4] - wait... 22:01:07.000 guizy.Pool [Thread-1] - borrow MockConnection{name='Connection 2'} 22:01:07.006 guizy.Pool [Thread-3] - wait... 22:01:07.099 guizy.Pool [Thread-0] - free MockConnection{name='Connection 1'} 22:01:07.099 guizy.Pool [Thread-2] - wait... 22:01:07.099 guizy.Pool [Thread-3] - borrow MockConnection{name='Connection 1'} 22:01:07.099 guizy.Pool [Thread-4] - wait... 22:01:07.581 guizy.Pool [Thread-3] - free MockConnection{name='Connection 1'} 22:01:07.582 guizy.Pool [Thread-2] - borrow MockConnection{name='Connection 1'} 22:01:07.582 guizy.Pool [Thread-4] - wait... 22:01:07.617 guizy.Pool [Thread-1] - free MockConnection{name='Connection 2'} 22:01:07.618 guizy.Pool [Thread-4] - borrow MockConnection{name='Connection 2'} 22:01:07.955 guizy.Pool [Thread-4] - free MockConnection{name='Connection 2'} 22:01:08.552 guizy.Pool [Thread-2] - free MockConnection{name='Connection 1'}
V. State
Stateless: The data stored by a member variable is also known as status information. Stateless means that there is no member variable
Servlets generally do not set member variables for Servlets to keep their threads safe. Classes that do not have any member variables are thread safe
Reference resources: