StringBuilder线程安全探讨

StringBuilder线程安全探讨

StringBuilder在多线程中可能会出现竞争


在多线程中,由于StringBuilder的方法不是同步的,所有线程都可以自由地访问并更改StringBuilder对象。

我们以一个简单的例子证明StringBuilder在多线程中不是线程安全的,我们通过将StringBuilder与线程安全的StringBuffer在多线程条件下执行同一个方法对比:

定义StringBuilder 与 StringBuffer

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");  
        }  
    }  
});

输出结果

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

完整代码

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());

输出结果:

StringBuilder Result: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaba aaaaaaabbbababababababababababab babababababababababababababababababababababababababababababababababababb abababb ba bababa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

StringBuffer Result: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababababababababababababababababababababababababababababababababababbabbabababababbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb

多次执行代码后,从输出结果来看,这两个字符串的长度相同。StringBuilder的输出结果中,多次出现资源丢失的情况,两个线程存在资源竞争,而StringBuffer的输出结果中则没有出现该情况。

StringBuilder的线程安全的实现形式


StringBuilder的线程安全的实现形式,有以下几种方式:

  1. 在StringBuilder的公共方法前添加synchronized关键字,定义如下的SafeStringBuilder
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();  
    }  
}

使用SafeStringBuilder代替StringBuilder

SafeStringBuilder sb = new SafeStringBuilder();
  1. 在线程中使用同步块,对于A、B线程,在循环前使用了synchronized关键字,以实现线程安全
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. 对每个线程定义单独的StringBuilder,以下方法中,对于A、B线程单独定义了一个StringBuilder对象
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. 使用线程安全的StringBuffer,使用StringBuffer是实现StringBuilder线程安全最简单的实现形式,在前面的代码中也有展示,不过多赘述。

猜你喜欢

转载自blog.csdn.net/m0_56170277/article/details/130248971