preface

Main contents of this paper:

  • This section describes the share mode
  • Example – Cloud disk
  • conclusion
  • The typical application of the source analysis model
    • The share mode in String
    • Integer Indicates the share element mode
    • Share schema in Long
    • Share mode in Apache Common Pool2

Recommended reading

Design patterns and typical application of design patterns | | simple factory pattern factory method pattern and a typical application of design patterns | the abstract factory pattern and a typical application of design pattern model and typical application design | | builders prototype model and typical application design model and typical application design | | appearance Decorator pattern and a typical application of design patterns | adapter pattern and typical applications

Visit my personal blog: laijianfeng.org


The flyweight pattern

Flyweight Pattern: the use of sharing technology effectively supports the reuse of a large number of fine-grained objects. The system only uses a small number of objects, and these objects are very similar, the state change is very small, can realize the object multiple reuse. Because the share mode requires that the objects that can be shared must be fine grained objects, it is also called lightweight mode, which is an object structure mode. The share mode has a complex structure and is generally used together with the factory mode.

role

Flyweight (abstract meta-class) : Usually an interface or abstract class in which the abstract meta-class declares methods common to the concrete meta-class. These methods can provide external data (internal state) of a meta-object and also set external data (external state) through these methods.

ConcreteFlyweight: Implements an abstract meta-class, called a meta-object; Storage space for internal state is provided in the concrete meta-class. Typically, we can design concrete meta-classes in conjunction with the singleton pattern, providing a unique meta-object for each concrete meta-class.

UnsharedConcreteFlyweight (non-shared specific flyweight category) : not all of the abstract metaclass subclasses needs to be Shared, cannot be Shared subclasses can be designed as a Shared specific flyweight classes; An object that does not share a concrete meta-class can be created by instantiation.

FlyweightFactory: The free meta-factory class is used to create and manage the free meta-objects. It is designed for abstract free meta-classes, and stores the concrete free meta-objects of various types in a free meta-pool. The free meta-pool is generally designed as a collection of “key value pairs” (it can also be other types of collections), which can be combined with the factory mode. When a user requests a concrete share object, the share factory provides an instance that is already created and stored in the share pool or creates a new instance (if one does not exist), returns the newly created instance and stores it in the share pool.

Pure share mode: In pure share mode, all concrete metaclasses are shareable, and no non-shared concrete metaclasses exist. Composite share mode: some simple share objects can be combined using the composite pattern to form composite share objects. Such composite share objects themselves cannot be shared, but they can be decomposed into simple share objects, and the latter can be shared

In the flyweight pattern introduces the flyweight factory class, the role of the flyweight factory class is to provide a used to store the flyweight objects the flyweight pool, when users need to object, first from the flyweight pool, if the flyweight pool does not exist, then create a new flyweight objects returned to the user, and save the new object in the flyweight pool.

The code for a typical meta-factory class looks like this:

Class FlyweightFactory {private flyweightMap flyweights = newHashMap(); Public Flyweight getFlyweight(String key){// If the object exists, it is directly obtained from the poolif(flyweights.containsKey(key)){
            return(Flyweight)flyweights.get(key); } // If the object does not exist, create a new object and add it to the pool, then returnelse {
            Flyweight fw = newConcreteFlyweight();
            flyweights.put(key,fw);
            returnfw; }}}Copy the code

The design of the metaclass is one of the key points of the metaclass. In the metaclass, the internal state and the external state should be treated separately. The internal state is usually treated as the member variable of the metaclass, while the external state is added to the metaclass by injection.

A typical enjoy metaclass code would look like this:

Class Flyweight {// Internal state intrinsicState as a member variable, the internal state of the same member object is the same private String intrinsicState; public Flyweight(String intrinsicState) { this.intrinsicState=intrinsicState; Public void operation(String extrinsicState) {//...... }}Copy the code

The general class diagram of the share mode is as follows

The sample

General network backup for the same file with only a, there is a scene such as: when we upload a movie that someone uploaded, will find upload finished soon, actually not really upload, but refer to others that have been uploaded the film, so we can improve the user experience, can save the storage space to avoid resource waste

Note: this scene is xiaobian thought, and the general see the example is not quite the same, xiaobian is not sure whether to enjoy the yuan mode, please give more advice

First define a utility class HashUtil, computing the contents hash value (note: the hash from www.cnblogs.com/oxgen/p/396… Is copied.)

public class HashUtil {
    public static String computeHashId(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        returncacheKey; } private static String bytesToHexString(byte[] bytes) { // http://stackoverflow.com/questions/332079 StringBuilder sb =  new StringBuilder();for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        returnsb.toString(); }}Copy the code

Resource class, which shares the internal state of the metaclass

public class Resource {
    private String hashId; private int byteSize; private String content; public Resource(String content) { this.content = content; this.hashId = HashUtil.computeHashId(content); / / filehashValue of enclosing byteSize = content. length (); } / /... Getter, setter, toString... }Copy the code

The user’s File class File, where resource is in the internal state and owner and filename are in the external state

public  class File {
    protected String owner;
    protected String filename;
    protected Resource resource;

    public File(String owner, String filename) {
        this.owner = owner;
        this.filename = filename;
    }

    public String fileMeta() {// The file is stored in the file systemif (this.owner == null || filename == null || resource == null) {
            return "Unknown file";
        }
        return owner + "-" + filename + resource.getHashId();
    }


    public String display() {
        return fileMeta() + ", resource content:"+ getResource().toString(); } / /... Getter, setter, toString... }Copy the code

PanServer, a web disk class, uses singleton mode (it also uses factory method mode in other examples). In the Upload method, it determines whether a file with the same content already exists according to the hashId of the uploaded file. If the file does exist, it will be referenced

public class PanServer { private static PanServer panServer = new PanServer(); // singleton mode private Map<String, Resource> resourceSystem; Private Map<String, File> fileSystem; // File system publicPanServer() {
        resourceSystem = new HashMap<String, Resource>();
        fileSystem = new HashMap<String, File>();
    }

    public static PanServer getInstance() {
        return panServer;
    }

    public String upload(String username, LocalFile localFile) {
        long startTime = System.currentTimeMillis();
        File file = new File(username, localFile.getFilename());
        String hashId = HashUtil.computeHashId(localFile.getContent()); // Compute filehashThe value System. Out.println (username +"Upload file");
        try {
            if (resourceSystem.containsKey(hashId)) {
                System.out.println(String.format("Identical file '%s' was detected. To save space, reuse file".localFile.getFilename()));
                file.setResource(this.resourceSystem.get(hashId));
                Thread.sleep(100);
            } else {
                System.out.println(String.format("File '%s' uploaded....".localFile.getFilename()));
                Resource newResource = new Resource(localFile.getContent()); file.setResource(newResource); this.resourceSystem.put(newResource.getHashId(), newResource); Thread.sleep(3000); thread.sleep (3000); }} catch (Exception e) {e.printStackTrace(); } fileSystem.put(file.fileMeta(), file); long endTime = System.currentTimeMillis(); System.out.println(String.format("File upload completed, total %s ms \n", endTime - startTime));
        return file.fileMeta();
    }


    public void download(String fileKey) {
        File file = this.fileSystem.get(fileKey);
        if (file == null) {
            System.out.println("File does not exist");
        } else {
            System.out.println("Download file:"+ file.display()); } // To LocalFile returns}}Copy the code

Client and local file classes

public class LocalFile { private String filename; private String content; public LocalFile(String filename, String content) { this.filename = filename; this.content = content; } / /... Omit... } public class Test { public static void main(String[] args) { PanServer panServer = PanServer.getInstance(); String fileContent ="This is a PDF of Design Patterns: From Getting Started to Giving up.";
        LocalFile localFile1 = new LocalFile("Xiao Ming's Design model.pdf", fileContent);
        String fikeKey1 = panServer.upload("Xiao Ming".localFile1);

        LocalFile localFile2 = new LocalFile("Daming's Design patterns. PDF", fileContent);
        String fikeKey2 = panServer.upload("Ming".localFile2); panServer.download(fikeKey1); panServer.download(fikeKey2); }}Copy the code

The output

Xiaoming upload file The design mode. PDF file is uploaded to.... Daming upload file: Daming design pattern.pdf The file with the same content was detected. To save space, reuse the file. When the file was uploaded, it took 100 ms to download the file: Xiaoming - Xiaoming design pattern.pdf-f73 ea50f00f87b42d1f2e4eb6b71d383, Resource content: Resource {hashId='f73ea50f00f87b42d1f2e4eb6b71d383', byteSize=22, content='This is a PDF of Design Patterns: From Getting Started to Giving up'} Download file: Daming - Daming design patterns.pdf-f73 ea50f00f87b42d1f2e4eb6b71d383, Resource content: Resource {hashId='f73ea50f00f87b42d1f2e4eb6b71d383', byteSize=22, content='This is a PDF of Design Patterns: From Getting Started to Giving up'}
Copy the code

Xiaoming and Daming uploaded a file respectively, the content of the file (internal state) is the same, but the name (external state) is different. Because the internal state is the same, there is no need to store it repeatedly, so a copy of the internal state is made

Enjoy yuan model summary

The main advantages of the share mode are as follows:

  • The system can greatly reduce the number of objects in the memory, so that only one copy of the same or similar objects can be saved in the memory, saving system resources and improving system performance.
  • The external state of the share schema is relatively independent and does not affect its internal state, allowing share objects to be shared in different environments.

The main disadvantages of the share mode are as follows:

  • The share pattern complicates the system, requiring the separation of internal and external states, which complicates the logic of the program.
  • In order for objects to be shareable, the share pattern externalizes part of the state of the share object, and reading the external state makes the run time longer.

Applicable scenarios:

  • A system has a large number of identical or similar objects, resulting in a large memory consumption.
  • Most of the state of an object can be externalized, and these external states can be passed into the object.
  • The use of the share pattern requires maintaining a pool of share objects, which requires a certain amount of system resources, so it should be worthwhile to use the share pattern only when the share objects need to be reused multiple times.

The typical application of the source analysis model

The share mode in String

Java defines the String class as final (immutable). JVM strings are usually stored in the String constant pool. Java ensures that there is only one copy of a String in the constant pool. The JVM takes it out of the permanent generation and places it in the heap.

Let’s take a test:

public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        String s3 = "he" + "llo";
        String s4 = "hel" + new String("lo");
        String s5 = new String("hello");
        String s6 = s5.intern();
        String s7 = "h";
        String s8 = "ello"; String s9 = s7 + s8; System.out.println(s1==s2); //trueSystem.out.println(s1==s3); //trueSystem.out.println(s1==s4); //falseSystem.out.println(s1==s9); //falseSystem.out.println(s4==s5); //falseSystem.out.println(s1==s6); //true}}Copy the code

When a String variable is created as a literal, the JVM puts the literal Hello into the String constant pool at compile time, which is loaded into memory when the Java program is started. The character of this string constant pool is that there is one and only one copy of the same literal. If there are other identical literals, the JVM returns a reference to that literal. If there are no identical literals, the literal is created in the string constant pool and returns a reference to it.

Since the literal hello to which S2 refers already exists in the constant pool (s1 precedes S2), the JVM returns a reference to the literal binding, so s1==s2.

The concatenation of s3 literals is essentially Hello, and the JVM optimizes it at compile time, so s1 and S3 are equal.

New String(“lo”) in S4 generates two objects, lo, new String(“lo”), lo in the String constant pool, new String(“lo”) in the heap, String s4 = “hel” + new String(“lo”) is essentially the addition of two objects, the compiler does not optimize, the result of the addition is stored in the heap, while s1 is stored in the String constant pool, of course not equal. S1 is equal to s9, same thing.

S4 ==s5 the sum of the two is both in the heap and, needless to say, definitely not equal.

S1 = = s6, s5. Intern () method can make a heap in the string during the running of dynamically added to the string constant pool (string constant pool is the content of the program launch was loaded ok), if the string constants in the pool of the object literal, it returns the literal string constant pool in the reference, otherwise, Creates a copy of the literal into the string constant pool and returns a reference to it. So s1==s6 prints true.

Integer Indicates the share element mode

The following is an example:

    public static void main(String[] args) {
        Integer i1 = 12 ;
        Integer i2 = 12 ;
        System.out.println(i1 == i2);

        Integer b1 = 128 ;
        Integer b2 = 128 ;
        System.out.println(b1 == b2);
    }
Copy the code

The output is

true
false
Copy the code

Why is the first true and the second false? Integer b1 = 128; It actually becomes Integer b1 = integer.valueof (128); So let’s look at the implementation of the valueOf method in Integer

public final class Integer extends Number implements Comparable<Integer> {
    public static Integer valueOf(int var0) {
        return var0 >= -128 && var0 <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[var0 + 128] : new Integer(var0);
    }
    / /... Omit...
}
Copy the code

IntegerCache cache class

// is a private static class inside Integer, where the cache[] is the Integer cached by the JDK. private static class IntegerCache { static final int low = -128; Static final int high; The default value is 127. You can also manually set vm parameters static final Integer cache[]; Static {// high value may be configured by property int h = 127; / / this virtual machine parameters can be set at runtime to determine h: - Djava. Lang. Integer. IntegerCache. High = 250 StringintegerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue ! = null) {int I = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); H = math.min (I, integer.max_value - (-low) -1); h = math.min (I, integer.max_value - (-low) -1); } high = h; cache = new Integer[(high - low) + 1]; int j = low; // The loop assigns the range to the cache[] arrayfor(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}}Copy the code

If valueOf is between -128 and 127, the subscript is calculated and returned from the cache. If valueOf is between -128 and 127, a new Integer object is created

Share schema in Long

public final class Long extends Number implements Comparable<Long> {
    public static Long valueOf(long var0) {
        return var0 >= -128L && var0 <= 127L ? Long.LongCache.cache[(int)var0 + 128] : new Long(var0);
    }   
    private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }
    //...
}
Copy the code

Similarly, there is a cache in Long, but you cannot specify a maximum cache

Share mode in Apache Commons Pool2

The basic idea of object pooling is to save used objects and reuse them the next time they are needed, thereby reducing some of the overhead associated with frequent object creation. Objects that serve as containers for holding objects are called Object pools (or pools for short)

Apache Commons Pool implements object pooling. Object generation, destruction, activation, passivation, and state transitions are defined, and several default object pool implementations are provided.

There are several important objects:

PooledObject (pool object) : Encapsulates objects (such as threads, database connections, TCP connections) into poolable objects. PooledObjectFactory (Pool object Factory) : Defines methods for manipulating the life cycle of PooledObject instances. PooledObjectFactory must be thread safe. Object Pool: The Object Pool manages PooledObject, such as lending objects, returning objects, verifying objects, how many active objects there are, and how many free objects there are.

Private final Map<S, PooledObject<S>> allObjects = new ConcurrentHashMap<S, PooledObject<S>>();Copy the code

Important methods:

BorrowObject: To loan an object from a pool. ReturnObject: Return an object to the pool.

Due to the length of the article, stay tuned for an introduction to using Apache Commons Pool2

Java: String String constant pool Integer Apache Commons pool2-2.4.2 source code learning notes Apache Commons pool2 source code analysis