Струнная нить безопасна? - PullRequest
       7

Струнная нить безопасна?

0 голосов
/ 27 февраля 2019

Строка является неизменной, что означает, что как только вы измените значение, создайте новую ссылку и оставите предыдущее ссылочное значение таким, как было.

Однако я не понимаю, когда кто-то спорит:

Строки являются поточно-ориентированными, поскольку они неизменны

Рассмотрим код ниже:

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 неизменяемые ссылки, каждая из которых является поточно-ориентированной, но как разработчик все еще не может использовать что-то неизменное и ожидатьend-result будет поточно-ориентированным.

ИМХО, неизменность не гарантирует безопасность потоков.

Может кто-нибудь объяснить мне, как String является поточно-ориентированным?

Обновление:

Спасибо за ваши ответы, я понимаю, что каждая строка можетбыть потокобезопасным, но я хотел сказать, что нет прямой связи между безопасностью потока и неизменяемостью, когда вы используете их в других методах.

например, неизменяемый объект может использоваться в объекте с состоянием и заканчиваться нане потокобезопасный результат, а также изменяемый объект можно использовать в синхронизированном методе и в результате получить потокобезопасный результат.

Ответы [ 4 ]

0 голосов
/ 28 февраля 2019

Я думаю, что это можно суммировать следующим образом:

  1. Операции над String объектами поточно-ориентированы.(Они потокобезопасны , потому что String объекты являются неизменяемыми, но , почему не имеет прямого отношения к вашему примеру.)
  2. Несинхронизированные операции чтения и записи 1 для не- final общих 2 переменных не являются потокобезопасными, независимо от типа переменной.

В вашем примере это str = str + 1;.Это объединяет операции над String объектами с операциями над несинхронизированной общей переменной (str).Это не является потокобезопасным из-за последнего.


1 - Точнее, операции, в которых нет , происходит до отношения между записью и чтением, чтобы гарантировать требуемую памятьсвойства видимости, и нет блокировки, чтобы гарантировать требуемые свойства атомарности.(«Обязательный» означает обязательный для правильности алгоритма ...)

2 - Совместно используемые средства, видимые и используемые более чем одним потоком.Если переменная видна только одному потоку или используется им, она называется ограниченной потоком и фактически не используется совместно.

0 голосов
/ 27 февраля 2019

Важно знать, как работает память на языках программирования.Переменная str не является строковым объектом, как вы могли бы подумать.Но вместо этого это ссылка на объект String с некоторым адресом.

Изменение того, на что указывает str , не изменяет строку, на которую оно указывает.На самом деле то, что происходит, выглядит примерно так:

У нас есть пул памяти, в нашем пуле три строки.У каждой строки есть адрес, который позволяет нам ее найти.

  • Строка 1 - «Привет», адрес: 0x449345
  • Строка 2 - «Там», Адрес: 0x058345
  • Строка 3 - «Мир», адрес: 0x004934

У нас есть переменная, которая указывает на каждую переменную, мы будем называть их a, b и c.

Если бы мы сказали: System.out.println(a); Java выдаст Hello.Но a не является «Привет» .Вместо этого a - это то, что содержит 0x449345 .Затем компьютер говорит: «Хорошо, я возьму то, что находится в 0x449345 и распечатаю его».Когда он идет и смотрит на этот адрес, он находит строку «Hello».

Однако, если бы вы сказали: a = "NEW STRING"; a не будет указывать ни на один из наших предыдущих адресов.Вместо этого создается новый адрес, и "NEW STRING" помещается в эту область памяти.

Так же работает сборка мусора в Java.Как только вы установите значение «NEW STRING», оно больше не будет указывать на 0x449345, это говорит сборщику мусора, что этот объект безопасно удалить.Вот как ваша программа очищается после себя и не потребляет тонны оперативной памяти.

Из-за этого ссылка , указывающая на строку, не является поточно-ориентированной но фактический объект ЕСТЬ!Любой неизменный объект является безопасным с точки зрения безопасности, поскольку вы НЕ МОЖЕТЕ изменять этот объект вообще, вы можете изменять только то, на что указывает ваша переменная.Вы должны указать на другой объект, чтобы полностью «изменить» ваш неизменный объект.

0 голосов
/ 27 февраля 2019

Ваша ссылка str не является неизменной, вы изменяете ее каждый раз, когда переназначаете ее значение.Поскольку вы разделяете изменчивое состояние между потоками без синхронизации или мьютекса, результат не является безопасным.

Следующее сработало для меня 5 раз, когда я пытался это сделать.Обратите внимание, что я добавил мьютекс вокруг конкатенации вашей строки.

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

Всегда печатает "1001".

0 голосов
/ 27 февраля 2019

Это не печать 1001, потому что это зависит от того, когда каждый поток принимает текущую ссылку на память str (потому что ссылка изменяемая , поэтому не является потоко-безопасной).

Посмотрите на этот пример, у нас есть 3 потока {T1, T2, T3}.

T1 получает ссылку на str и меняет ее так, что мы имеем str = "11";T2 и T3 получают str ссылку (одновременно) и изменяют ее так, что теперь у вас есть, T2 -> str= "111" и T3 -> str = "111";

Когда str обновляетсяможет быть обновлено значением str из T2 или T3 (зависит от выполнения), но, по сути, вы не можете думать, что каждый поток последовательно выполняет операцию.Таким образом, String являются неизменяемыми, поэтому потокобезопасными, потому что каждый поток просто изменяет свою собственную ссылку, но вы должны синхронизировать логику обновления, если вам это нужно.Если вы хотите напечатать 1001 из своего кода, вам нужно синхронизировать доступ к str (мониторы, блокировки, семафоры, синхронизированное ключевое слово и т. Д.).

Btw, String поточно-ориентированы, потому что если выпопытайтесь изменить его (любым способом), вы создадите другую ссылку на память, чтобы два (или более) потока не могли манипулировать одной и той же ссылкой String или, что лучше, они имеют одинаковую ссылку на строку, но когда они манипулируют ею, новая строка сохраняется вновая ссылка).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...