Синхронизация, когда использовать или не использовать?
Я начал изучать совместимость и потоки в Java. Я знаю основы синхронизированного (то есть, что он делает). Концептуально я понимаю, что он обеспечивает взаимный эксклюзивный доступ к общему ресурсу с несколькими потоками в Java. Но когда вы сталкиваетесь с примером, как показано ниже, я смущен, это хорошая идея, чтобы он синхронизировался. Я знаю, что критические разделы кода должны быть синхронизированы, и это ключевое слово не должно чрезмерно использоваться или оно влияет на производительность.
public static synchronized List<AClass> sortA(AClass[] aArray)
{
List<AClass> aObj = getList(aArray);
Collections.sort(aObj, new AComparator());
return aObj;
}
public static synchronized List<AClass> getList(AClass[] anArray)
{
//It converts an array to a list and returns
}
Ответы
Ответ 1
Предполагая, что каждый поток передает другой массив, тогда никакой синхронизации не требуется, поскольку остальные переменные являются локальными.
Если вместо этого вы запускаете несколько потоков, все вызывающие sortA
и передавая ссылку на один и тот же массив, у вас будут проблемы без synchronized
, потому что они будут мешать друг другу.
Остерегайтесь, что из примера, что метод getList
возвращает новый List
из массива, так что даже если потоки передают один и тот же массив, вы получаете разные объекты List
. Это вводит в заблуждение. Например, используя Arrays.asList
создает List
, поддерживаемый данным массивом, но javadoc четко заявляет, что Changes to the returned list "write through" to the array.
поэтому будьте осторожны.
Ответ 2
Синхронизация обычно необходима, когда вы делитесь данными между несколькими вызовами, и есть вероятность, что данные будут изменены, что приведет к несогласованности. Если данные доступны только для чтения, вам не нужно синхронизировать.
В приведенном выше фрагменте кода данных нет. Методы работают на введенном входе и возвращают результат. Если несколько потоков вызывают один из ваших методов, каждый вызов будет иметь свой собственный вход и выход. Следовательно, нет никаких шансов на постоянство в любом месте. Таким образом, ваши методы в приведенном выше фрагменте не должны синхронизироваться.
Синхронизация, если это неоправданно используется, приведет к ухудшению производительности из-за связанных с этим накладных расходов и, следовательно, следует осторожно использовать только при необходимости.
Ответ 3
Ваши статические методы не зависят от какого-либо общего состояния, поэтому не нужно синхронизировать.
Ответ 4
Не существует правила, определенного как использовать синхронизацию, а когда нет, когда вы уверены, что ваш код не будет доступен для одновременных потоков, вы можете избежать использования синхронизированного.
Ответ 5
Синхронизация, как вы правильно определили, влияет на пропускную способность вашего приложения, а также может привести к голоданию.
Все в основном должны быть неблокируемыми, поскольку коллекции из пакета concurrency реализованы.
Как и в вашем примере, все вызывающие потоки передают там собственную копию массива, getList
не нужно синхронизировать, поэтому метод sortA
, поскольку все остальные переменные являются локальными.
Локальные переменные живут в стеке, и каждый поток имеет свой собственный стек, поэтому другие потоки не могут помешать ему.
Вам требуется синхронизация, когда вы изменяете состояние объекта, которое другие потоки должны видеть в согласованном состоянии, если ваши вызовы не изменяют состояние объекта, для которого вам не нужна синхронизация.
Ответ 6
Я бы не использовал synchronized
для однопоточного кода. т.е. когда нет возможности, к которому объект будет доступен несколькими потоками.
Это может показаться очевидным, но ~ 99% StringBuffer, используемое в JDK, может использоваться только одним потоком, может быть заменено на StringBuilder (который не синхронизирован)