Java 由于不变性,字符串线程安全?

Java 由于不变性,字符串线程安全?,java,string,immutability,Java,String,Immutability,Hi读到关于字符串是线程安全的,因为它是不可变的 例如,我: String a = "test"; 一个线程使用这个变量。 但另一个线程也可以使用此变量并对其进行更改: a = a + "something"; 那么它是否会改变 如果它是易变的,我会得到它,它一次只能被一个线程使用。但是免疫并不能保证我能做到这一点 通过让每个线程复制引用a,您可以使代码线程非常安全。事实上,这通常都会发生,因为您通常通过参数将字符串传递给线程 因此,两个线程都持有对原始字符串的引用,这里是“test”。如果

Hi读到关于字符串是线程安全的,因为它是不可变的

例如,我:

String a = "test";
一个线程使用这个变量。 但另一个线程也可以使用此变量并对其进行更改:

a = a + "something";
那么它是否会改变


如果它是易变的,我会得到它,它一次只能被一个线程使用。但是免疫并不能保证我能做到这一点

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


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

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

final String a = "test";

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

String a = "test";
这里
a
指向一个字符串
“test”

这里创建了一个新字符串,作为连接
“test”
“something”
的结果,其中
“testsomething”
,其中a指向。这是另一个例子


因此不存在线程安全问题,因为两个线程都有自己的
a
引用相同的
“test”
字符串对象,但一旦其中一个线程修改字符串引用
“testsomething”
对象,另一个线程仍将引用原始的
“test”
对象。

字符串对象是线程安全的。如果
字符串a
是局部变量,则此代码仍然是线程安全的。如果它是您的类的一个字段,那么您有责任保证它的线程安全。字符串的线程安全性不会神奇地使您自己的代码线程安全。你应该小心点

您可以使字段不稳定,然后在线程之间获得可见性。所以任何线程都会看到字段的最新值。但你不会以这种方式获得原子性。想象一下下面的情景。让
a=“test”
。线程1更新a,线程2更新a。它们都可以看到当前值,即
“test”
。他们读取它,通过连接生成新字符串,并更新
a
的值。那么这个值是多少呢?这是未知的。如果线程严格地一个接一个地执行操作,那么它可以是“testsomethingsomething”。但是它可以是
“testsomething”
。例如:

  • 线程1从
    a
  • 线程2从
    a
  • 线程2使用“testsomething”更新
    a
  • 线程1使用相同的
    “testsomething”
    更新
    a
    (记住,它之前将
    a
    读为
    “test”

瞧,你失去了你的领域的更新。为了避免这种问题,您应该通过在单个锁对象上同步来保护对字段的所有访问和修改。

这里有很多混乱

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

在我们的例子中,这只是一个保证,我们最终得到了一个“testsomething”,但不是像这样的混乱 “tsomethingest”或“tesomethingst”或“Tsosmtething”或“somethingst”

这里有一个“快速而肮脏”的说明:

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

}    

它将改变,因为
a
将采用一个新值,变量“a”将引用另一个变量。不会变异,我知道它会改变的。但是如果它会改变,那么如果两个线程可以改变它的值,那么它就不是线程安全的?在一个线程安全的数据结构和一个线程安全的代码之间有很大的区别。啊,好的!这也许是我遗漏的一点。我想如果我发现一个字符串,就不会创建新的字符串。如果创建了一个新字符串,那么问题就解决了。您可以使用eclipse的调试器轻松地进行检查,该调试器显示了引用的哈希和地址。我如何在调试器中检查引用?据我所知,它位于
Variables
视图中。我没有在我当前的计算机上安装eclipse,所以我不能告诉您确切的位置。但它在(cf)中,好吧,没关系,我只是对每个变量的hashcode做了一个sys println!例如,我将其用作HttpServlet中类的字段。我必须使它易失性,使其线程安全,对吗?但作为局部变量,它始终是线程安全的!?如果是本地的,一切都好。无法从其他线程访问局部变量。如果这是一个领域,那就取决于你想要达到的目标。如果您将其设置为易失性,那么所有线程都会看到它的更改。如果它足以完成任务,那么是的,您的代码将是线程安全的。@krackmoe,您当前的代码可能已经丢失了更新。为了避免这种情况,您需要使串联原子化。
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