Java字符串线程安全吗?

时间:2020-01-09 10:34:53  来源:igfitidea点击:

在多线程环境中,共享对象可以由任何线程修改,在某些情况下,我们可能需要确保线程之间共享的原始对象保持不变。可以通过使该对象不可变来实现。由于Java中的String在设计上是不可变的,因此它也是线程安全的,因此可以在许多线程之间安全地共享字符串对象。

Java String不变性和线程安全

需要注意的重要一点是,即使String是不可变的,因此是线程安全的,对String对象的引用也不是线程安全的。
如果将String对象传递给线程,并在线程中对其进行了修改,则将创建新的String并更改引用,但原始String保持不变。我们将通过一个示例来澄清这一点。

在示例中,字符串对象在三个线程之间共享。在执行这些线程时,共享字符串对象将通过在其后面添加内容来进行修改。当所有线程完成后,在主线程中再次打印字符串对象,以验证其是否保持不变。

public class StringThreadSafeExp implements Runnable {
  private String str;
  public StringThreadSafeExp(String str){
    this.str = str;
  }
  @Override
  public void run() {
    System.out.println("Executing Thread- " + Thread.currentThread().getName());        
    // Adding to String  
    str = str + " World";
    System.out.println("Modified String " + str);
  }

  public static void main(String[] args) {
    String str = "Hello";

    Thread t1 = new Thread(new StringThreadSafeExp(str));
    Thread t2 = new Thread(new StringThreadSafeExp(str));
    Thread t3 = new Thread(new StringThreadSafeExp(str));
    t1.start();
    t2.start();
    t3.start();
    // Wait for all threads to terminate
    try {
      t1.join();
      t2.join();
      t3.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
    System.out.println("Original String is " + str);
  }
}

输出:

Executing Thread- Thread-2
Executing Thread- Thread-1
Executing Thread- Thread-0
Modified String Hello World
Modified String Hello World
Modified String Hello World
Original String is Hello

如我们所见,每个线程在修改传递的String时都会引用指向指向修改内容的新String对象,而原始字符串则保持不变。

Java String的不变性确保String一旦赋值,就无法修改。通过使用可变的StringBuffer对象,我们可以验证在线程之间共享和修改可变对象时发生的情况。

public class StringThreadSafeExp implements Runnable {
  private StringBuffer sb;
  public StringThreadSafeExp(StringBuffer sb){
    this.sb = sb;
  }
  @Override
  public void run() {
    System.out.println("Executing Thread- " + Thread.currentThread().getName());        
    // Adding to String  
    sb.append(" World");
    System.out.println("Modified String " + sb);
  }

  public static void main(String[] args) {
    StringBuffer sb = new StringBuffer("Hello");

    Thread t1 = new Thread(new StringThreadSafeExp(sb));
    Thread t2 = new Thread(new StringThreadSafeExp(sb));
    Thread t3 = new Thread(new StringThreadSafeExp(sb));
    t1.start();
    t2.start();
    t3.start();
    // Wait for all threads to terminate
    try {
      t1.join();
      t2.join();
      t3.join();
    } catch (InterruptedException e) {    
      e.printStackTrace();
    }
      System.out.println("Original String is " + sb);
  }
}

输出:

Executing Thread- Thread-0
Executing Thread- Thread-2
Executing Thread- Thread-1
Modified String Hello World World
Modified String Hello World
Modified String Hello World World World
Original String is Hello World World World

如我们现在所见,原始的StringBuffer对象本身被修改为可变的。