Безопасность резьбы для статических переменных
class ABC implements Runnable {
private static int a;
private static int b;
public void run() {
}
}
У меня есть класс Java, как указано выше. У меня несколько потоков этого класса. В методе run()
переменные a
и b
увеличиваются каждый раз в несколько раз. На каждом приращении я помещаю эти переменные в Hashtable.
Следовательно, каждый поток будет увеличивать обе переменные и помещать их в Hashtable. Как я могу сделать эти операции потоками безопасными?
Ответы
Ответ 1
Я бы использовал AtomicInteger, который был спроектирован так, чтобы быть потокобезопасным и мертв, легко использовать и придает абсолютный минимум синхронизации служебные данные для приложения:
class ABC implements Runnable {
private static AtomicInteger a;
private static AtomicInteger b;
public void run() {
// effectively a++, but no need for explicit synchronization!
a.incrementAndGet();
}
}
// In some other thread:
int i = ABC.a.intValue(); // thread-safe without explicit synchronization
Ответ 2
В зависимости от того, что должно быть потокобезопасным. Для этих примитивов int
вам нужно либо заменить их на AtomicInteger
, либо работать только с ними в методе или блоке synchronized
. Если вам нужно сделать поточный поток Hashtable для потоковой передачи, вам не нужно ничего делать, поскольку он уже синхронизирован.
Ответ 3
Используйте метод synchronized
, например.
public synchronized void increment()
{
a++; b++;
// push in to hash table.
}
Вышеприведенное хорошо, если вы получаете доступ к статике через один экземпляр, однако, если у вас есть несколько экземпляров, вам нужно синхронизировать какой-либо статический объект - что-то вроде (непроверено).
private static Object lock = new Object();
в методе
public void increment()
{
synchronize(lock)
{
a++;b++;
// do stuff
}
}
ПРИМЕЧАНИЕ. Эти подходы предполагают, что вы хотите увеличивать a
и b
за одно атомное действие, другие ответы показывают, как их можно индивидуально увеличить с помощью атомистики.
Ответ 4
Я хотел бы добавить некоторые сведения о ответе на этот вопрос.
Во-первых, OP спрашивает о:
Как я могу сделать эти операции потоками безопасными?
Прежде чем ответить на этот вопрос, нам нужно достичь согласованности в отношении потокобезопасности. Определение, которое мне больше всего нравится, состоит в том, что из "Java Concurrency In Practice" и
Класс является потокобезопасным, если он корректно ведет себя при доступе из нескольких потоков, независимо от планирования или чередование выполнения этих потоков во время выполнения окружающей среды и без дополнительной синхронизации или другой координация со стороны вызывающего кода.
Если вы согласны с определением, это означает, что мы достигаем согласованности перед дальнейшим обсуждением. Вернитесь к операциям, которые вы имели в виду, это a++
и b++
, после приращения вы поместите их в HashTable
.
Для операции a++
это не единственная операция. Он подчиняется модели read edit write. Как вы можете видеть, на самом деле он содержит три отдельных шага. Прочитайте значение, добавьте его и сохраните. Если у вас есть два потока, прочитайте переменную a
со значением 1
в одно и то же время после изменений и операций возврата. Значение будет 2
, но оно должно быть 3
. Чтобы избежать такой ситуации, как и другие, вы можете использовать тип AtomicInteger
вместо int
. AtomicInteger
будет гарантировать, что некоторые операции, такие как приращение, выполняются атомарно. Это означает, что операции read edit write не могут быть разделены и будут выполняться как один отдельный шаг.
После этого OP хочет сохранить значение в HashTable. HashTable - это поточно-безопасный контейнер, никакой другой синхронизации не требуется.
Надеюсь, это разъяснение может помочь кому-то другим способом. Спасибо.