字符串线程安全性因为不变性?

时间:2013-06-22 22:09:53

标签: java string immutability

您正在阅读字符串是线程安全的,因为它是不可变的。

例如我做:

String a = "test";

一个线程使用此变量。 但另一个线程仍然可以使用此变量并更改它:

a = a + "something";

所以它会改变吗?

如果它是易失性的,我会得到它,它一次只能被一个线程使用。但不可靠性并不能保证我这个!?

5 个答案:

答案 0 :(得分:5)

您没有更改a指向的对象,但a指向的位置:

String a = "test";

此处a指向字符串"test"

a = a + "something";

这里创建了一个新字符串,作为"test""something"串联的结果,"testsomething"指向的地方。a这是一个不同的例子。

因此没有线程安全的问题,因为两个线程都有自己的"test"引用相同的"testsomething"字符串对象,但是一旦其中一个线程将修改字符串引用"test"对象,另一个线程仍将引用原始{{1}}对象。

答案 1 :(得分:2)

字符串本身没有改变,引用是。听起来你需要参考是最终的。不变性保证了对象不会改变,而不是参考不能改变。只需将其标记为:

final String a = "test";

答案 2 :(得分:0)

通过让每个线程复制引用a,您可以轻松地使代码线程安全。事实上,这通常是发生的事情,因为你通常通过参数将字符串传递给线程。

因此两个线程都持有对原始字符串的引用,此处为"test"。如果线程1现在修改a,它只会修改此引用。线程2仍然保留对"test"的完整引用,因为字符串本身(而不是引用)是不可变的。

答案 3 :(得分:0)

字符串对象是线程安全的。如果您的String a是局部变量,那么此代码仍然是线程安全的。如果它是您班级的一个领域,那么您有责任保证其线程安全。 String的线程安全性不会神奇地使您自己的代码线程安全。你应该好好照顾它。

您可以使字段变为volatile,然后在线程中获得可见性。因此,任何线程都会看到您的字段的最新值。但是你不会以这种方式获得原子性。想象一下以下内容。让a = "test"。线程1更新a和线程2更新a。他们都看到当前值"test"。他们读取它,通过连接创建新字符串并更新a的值。这个价值会是什么?这是未知的。如果线程严格依次执行其操作,则可以是"testsomethingsomething"。但它可能只是"testsomething"。例如:

  • 主题1从"test"
  • 读取a
  • 主题2从"test"
  • 读取a
  • 主题2使用a
  • 更新"testsomething"
  • 主题1使用相同的a
  • 更新a(请记住,之前"test""testsomething"

Voila,你已经失去了对你的领域的更新。为避免此类问题,您应该使用单个锁定对象上的同步来保护对字段的所有访问和修改。

答案 4 :(得分:0)

这里有很多困惑......

某些类的线程安全性意味着并发使用它的实例 不会破坏它的内部结构。

在我们的案例中,我们终于得到了一个" testsomething"这只是保修,但不是像 " tsomethingest"或" tesomethingst"或" tseosmtething"或者"测试的东西"。

这里有一个快速而又脏兮兮的"插图:

public class Test2 {

    private volatile String tstStr = "";

    Test2(){
    }

    void impl(int par){
        Thread wrk = new Thread(new MyRun(par));
        wrk.start();
    }

    static public void main(String[] args) throws Exception {
       Test2 tst2 = new Test2();
       long startTime = System.currentTimeMillis();
       Thread wrk;
       for (int i = 0; i < 9; i=i+1) {
           tst2.impl(i);
       }
       long endTime = System.currentTimeMillis();
       System.out.println("The process took " + (endTime - startTime) + " milliseconds");
    }

    class MyRun implements Runnable {
        int no;
        MyRun(int var){
            no = var;
        }
        public void run(){
            tstStr = tstStr + " " + no;
            for (int i = 0; i < 3; i=i+1) {
                System.out.println("Message from "+no+", tested string ="+tstStr);
            }
        }
    }

}    

输出:

Message from 1, tested string = 0
Message from 1, tested string = 0 2 3
Message from 1, tested string = 0 2 3
Message from 4, tested string = 0 2 3 4
Message from 4, tested string = 0 2 3 4
Message from 0, tested string = 0 2
Message from 8, tested string = 0 2 3 4 7 8
Message from 5, tested string = 0 2 3 4 7 8 5
Message from 0, tested string = 0 2 3 4 7 8 5
Message from 0, tested string = 0 2 3 4 7 8 5
The process took 0 milliseconds
Message from 7, tested string = 0 2 3 4 7
Message from 7, tested string = 0 2 3 4 7 8 5 6
Message from 4, tested string = 0 2 3 4
Message from 3, tested string = 0 2 3
Message from 2, tested string = 0 2
Message from 3, tested string = 0 2 3 4 7 8 5 6
Message from 7, tested string = 0 2 3 4 7 8 5 6
Message from 6, tested string = 0 2 3 4 7 8 5 6
Message from 5, tested string = 0 2 3 4 7 8 5
Message from 8, tested string = 0 2 3 4 7 8 5
Message from 5, tested string = 0 2 3 4 7 8 5 6
Message from 6, tested string = 0 2 3 4 7 8 5 6
Message from 3, tested string = 0 2 3 4 7 8 5 6
Message from 2, tested string = 0 2 3 4 7 8 5 6
Message from 6, tested string = 0 2 3 4 7 8 5 6
Message from 8, tested string = 0 2 3 4 7 8 5 6
Message from 2, tested string = 0 2 3 4 7 8 5 6