Discussion sur la sécurité des threads de StringBuilder

Discussion sur la sécurité des threads de StringBuilder

StringBuilder peut concurrencer le multithreading


En multi-threading, puisque les méthodes de StringBuilder ne sont pas synchronisées, tous les threads peuvent librement accéder et modifier l'objet StringBuilder.

Nous utilisons un exemple simple pour prouver que StringBuilder n'est pas thread-safe en multithreading. Nous comparons StringBuilder avec StringBuffer thread-safe pour exécuter la même méthode dans des conditions de multithreading :

Définir StringBuilder et StringBuffer

StringBuilder stringBuilder = new StringBuilder();
StringBuffer stringBuffer = new StringBuffer();  

Simuler le scénario de la concurrence des ressources multi-thread

Thread t1 = new Thread(new Runnable() {
    
      
    @Override  
    public void run() {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("a");  
            stringBuffer.append("a");  
        }  
    }  
});  
  
Thread t2 = new Thread(new Runnable() {
    
      
    @Override  
    public void run() {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("b");  
            stringBuffer.append("b");  
        }  
    }  
});

résultat de sortie

System.out.println("StringBuilder Result: " + stringBuilder.toString());  
System.out.println("StringBuffer Result: " + stringBuffer.toString());

code complet

StringBuilder stringBuilder = new StringBuilder();  
StringBuffer stringBuffer = new StringBuffer();  
  
Thread t1 = new Thread(new Runnable() {
    
      
    @Override  
    public void run() {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("a");  
            stringBuffer.append("a");  
        }  
    }  
});  
  
Thread t2 = new Thread(new Runnable() {
    
      
    @Override  
    public void run() {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("b");  
            stringBuffer.append("b");  
        }  
    }  
});  
  
t1.start();  
t2.start();  
try {
    
      
    t1.join();  
    t2.join();  
} catch (InterruptedException e) {
    
      
    e.printStackTrace();  
}  
  
System.out.println("StringBuilder Result: " + stringBuilder.toString());  
System.out.println("StringBuffer Result: " + stringBuffer.toString());

Résultat de sortie :

StringBuilder Result: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaba aaaaaaabbbababababababababababab babababababababababababababababababababababababababababababababababababb abababb ba bababa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

StringBuffer Result: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababababababababababababababababababababababababababababababababababbabbabababababbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

Après avoir exécuté le code plusieurs fois, à en juger par la sortie, les deux chaînes ont la même longueur. Dans la sortie de StringBuilder, les ressources sont perdues plusieurs fois et il existe une concurrence de ressources entre deux threads, mais dans la sortie de StringBuffer, cette situation n'apparaît pas.

Implémentation thread-safe de StringBuilder


L'implémentation thread-safe de StringBuilder utilise les méthodes suivantes :

  1. Ajoutez le mot clé synchronized avant la méthode publique de StringBuilder et définissez SafeStringBuilder comme suit
public class SafeStringBuilder {
    
      
    private StringBuilder stringBuilder = new StringBuilder();  
  
    public synchronized void append(String str) {
    
      
        stringBuilder.append(str);  
    }  
  
    public synchronized void insert(int offset, String str) {
    
      
        stringBuilder.insert(offset, str);  
    }  
  
    public synchronized void delete(int start, int end) {
    
      
        stringBuilder.delete(start, end);  
    }  
  
    public synchronized void replace(int start, int end, String str) {
    
      
        stringBuilder.replace(start, end, str);  
    }  
  
    public synchronized void reverse() {
    
      
        stringBuilder.reverse();  
    }  
  
    public synchronized String toString() {
    
      
        return stringBuilder.toString();  
    }  
}

Utiliser SafeStringBuilder au lieu de StringBuilder

SafeStringBuilder sb = new SafeStringBuilder();
  1. Utiliser des blocs de synchronisation dans les threads. Pour les threads A et B, le mot clé synchronized est utilisé avant la boucle pour assurer la sécurité des threads
Runnable appendA = () -> {
    
      
    synchronized (stringBuilder) {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("a");  
        }  
    }   
};  
Runnable appendB = () -> {
    
      
    synchronized (stringBuilder) {
    
      
        for (int i = 0; i < 100; i++) {
    
      
            stringBuilder.append("b");  
        }  
    }  
};
  1. Définissez un StringBuilder distinct pour chaque thread. Dans la méthode suivante, un objet StringBuilder est défini séparément pour les threads A et B.
StringBuilder sb = new StringBuilder();  
  
Runnable appendA = () -> {
    
      
    StringBuilder stringBuilder = new StringBuilder();  
  
    for (int i = 0; i < 100; i++) {
    
      
        stringBuilder.append("a");  
    }  
  
    sb.append(stringBuilder);  
  
  
};  
Runnable appendB = () -> {
    
      
    StringBuilder stringBuilder = new StringBuilder();  
  
    for (int i = 0; i < 100; i++) {
    
      
        stringBuilder.append("b");  
    }  
  
    sb.append(stringBuilder);  
};
  1. À l'aide de StringBuffer thread-safe, l'utilisation de StringBuffer est la forme d'implémentation la plus simple pour atteindre la sécurité des threads StringBuilder, qui est également illustrée dans le code précédent, mais je vais le répéter ici.

おすすめ

転載: blog.csdn.net/m0_56170277/article/details/130248971