Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 线安全吗?_Java_Multithreading_Thread Safety - Fatal编程技术网

Java 线安全吗?

Java 线安全吗?,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,字符串是不可变的,这意味着一旦修改了它的值,就会创建一个新的引用,并保持以前的引用值不变 然而,当有人争论时,我不明白: 字符串是线程安全的,因为它们是不可变的 考虑以下代码: private String str = "1"; ExecutorService executorService = Executors.newFixedThreadPool(10); IntStream.range(0, 1000).forEach((i)-> executorService.submit(()

字符串是不可变的,这意味着一旦修改了它的值,就会创建一个新的引用,并保持以前的引用值不变

然而,当有人争论时,我不明白:

字符串是线程安全的,因为它们是不可变的

考虑以下代码:

private String str = "1";
ExecutorService executorService = Executors.newFixedThreadPool(10);
IntStream.range(0, 1000).forEach((i)-> executorService.submit(()-> {
    str = str +"1";
}));

executorService.awaitTermination(10, TimeUnit.SECONDS);

System.out.println(str.length());
如果它是线程安全的,那么它应该打印
1001
,而打印值总是小于预期值

我知道上面的代码将创建
1001
不可变引用,每个引用本身都是线程安全的,但是作为开发人员,仍然不能使用不可变的东西,并且期望
最终结果将是线程安全的

不变性并不能保证线程安全

有人能给我解释一下绳子是如何安全的吗

更新:

感谢您的回答,我知道每个字符串都可以是线程安全的,但我的观点是,当您在其他方法中使用它们时,线程安全性和不变性之间没有直接关系


例如,一个不可变对象可以在有状态对象中使用并以非线程安全的结果结束,另一个可变对象可以在同步方法中使用并以线程安全的结果结束

它不打印
1001
,因为它取决于每个线程何时使用
str
的当前内存引用(因为引用是可变的,所以不是线程安全的)

看看这个例子,我们有3个线程{T1,T2,T3}

T1获取str引用并将其更改为str=“11”; T2和T3获得
str
reference(同时)并将其更改为T2->
str=“111”
和T3->
str=“111”

更新
str
时,可以使用T2或T3中的str值进行更新(取决于执行情况),但本质上,您不能认为每个线程都顺序执行操作。 因此
String
s是不可变的,因此线程是安全的,因为每个线程只修改它们自己的引用,但是如果需要,您必须同步更新逻辑。 如果要从代码中打印
1001
,则需要同步对str的访问(监视器、锁、信号量、同步关键字等)


顺便说一句,
String
是线程安全的,因为如果您尝试(以任何方式)更改它,您将创建另一个内存引用,因此两个(或更多)线程不能操作相同的
字符串
引用,或者更好,它们有相同的字符串引用,但当它们操作它时,新字符串存储在新引用中)。

它不打印
1001
,因为它取决于每个线程何时使用
str的当前内存引用(因为引用是可变的,所以不是线程安全的)

看看这个例子,我们有3个线程{T1,T2,T3}

T1获取str引用并将其更改为str=“11”; T2和T3获得
str
reference(同时)并将其更改为T2->
str=“111”
和T3->
str=“111”

更新
str
时,可以使用T2或T3中的str值进行更新(取决于执行情况),但本质上,您不能认为每个线程都顺序执行操作。 因此
String
s是不可变的,因此线程是安全的,因为每个线程只修改它们自己的引用,但是如果需要,您必须同步更新逻辑。 如果要从代码中打印
1001
,则需要同步对str的访问(监视器、锁、信号量、同步关键字等)


顺便说一句,
String
是线程安全的,因为如果您尝试更改它(以任何方式),您将创建另一个内存引用,因此两个(或更多)线程无法操作相同的
String
引用,或者更好,它们具有相同的字符串引用,但当它们操作它时,新字符串存储在新引用中)了解编程语言中内存的工作原理是很重要的。变量str不像您想象的那样是字符串对象。但它是对字符串对象的引用,带有一些地址

修改str指向的内容不会修改它指向的字符串。事实上,情况是这样的:

我们有一个内存池,内存池中有三个字符串。每个字符串都有一个允许我们查找它的地址

  • 字符串1-“你好”,地址:0x449345
  • 字符串2-“在那里”,地址:0x058345
  • 字符串3-“世界”,地址:0x004934
我们有一个变量指向每一个变量,我们称它们为a、b和c

如果我们说:
System.out.println(a)
Java将打印
Hello
。但是a不是“你好”。相反,a是包含0x449345的内容。然后,计算机会说:“好的,我去把0x449345的内容打印出来。”当它去查看那个地址时,它会找到字符串“Hello”

但是,如果您说:
a=“newstring”a不会指向我们以前的任何地址。而是创建一个新地址,并将“新字符串”放在该内存位置内

这也是Java中垃圾收集的工作原理。一旦设置了一个等于“newstring”的值,它就不再指向0x449345,这会告诉垃圾收集器该对象可以安全删除。这就是您的程序在自身清理之后的清理方式,并且不会消耗成吨的RAM


因此,指向字符串的引用不是线程安全的,但实际对象是!任何不可变对象都是安全的,因为您根本无法修改该对象,您只能修改变量指向的对象。您必须指向一个完全不同的对象来“修改”您的不可变对象

了解内存在编程中的工作原理很重要
public class QuickyTest {

   private static String str = "1";

   public static void main( String[] args ) throws Exception {
      ExecutorService executorService = Executors.newFixedThreadPool( 10 );

      IntStream.range( 0, 1000 ).forEach( ( i ) -> executorService.submit( () -> {
         append( "1" );
      }
      ) );
      executorService.awaitTermination( 10, TimeUnit.SECONDS );
      System.out.println( str.length() );
      executorService.shutdown();
   }

   private static synchronized void append( String s ) {
      str = str + s;
   }
}