Потокобезопасность — это вопрос, который мы должны в первую очередь четко рассмотреть, когда занимаемся параллельным программированием на Java. Этот класс отлично работает в однопоточной среде, поэтому можем ли мы быть уверены, что он ведет себя правильно в случае многопоточного параллелизма?
Раньше у меня не было побочного бизнеса, я был полностью сосредоточен на своей работе, поэтому справлялся с ней очень хорошо, и мой менталитет всегда был хорошим, но после того, как у меня появился побочный бизнес, мой менталитет стал похож на американские горки. Когда доход от побочного бизнеса превышает доход от основного бизнеса, люди очень возбуждены, как куриная кровь.
Кажется, что я могу быть только однопоточным человеком.Когда многопоточный режим включается параллельно с побочным бизнесом и основным бизнесом, я становлюсь особенно неуверенным, хотя общий доход значительно улучшился по сравнению с до побочный бизнес.
Как заставить себя чувствовать себя в безопасности, я пока не придумал (если есть хороший способ, дайте знать). А вот как сделать класс безопасным в многопоточной среде, есть 3 правила, скажу я вам:
1. Не делитесь переменными состояния между потоками. 2. Измените переменную состояния на неизменяемую. 3. Используйте синхронизацию при доступе к переменным состояния.
Тогда вы можете спросить, что такое переменные состояния?
Давайте сначала посмотрим на класс без переменных состояния.Пример кода выглядит следующим образом.
class Chenmo {
public void write() {
System.out.println("我寻了半生的春天,你一笑便是了。");
}
}
Класс Chenmo не имеет состояния, у него есть только один метод, ни переменных-членов, ни переменных класса. Любой поток, обращающийся к нему, не повлияет на результаты другого потока, поскольку между двумя потоками нет общих переменных состояния. Таким образом, мы можем сделать следующий вывод:Классы без переменных состояния должны быть потокобезопасными..
Затем мы смотрим на класс с переменными состояния. Предположим, что молчание (класс Chenmo) записывает строку слов (write()
метод), необходимо провести статистику, чтобы найти издателя, который потребует плату за рукопись. Добавляем статистическое поле в класс Chenmo, пример кода следующий.
class Chenmo {
private long count = 0;
public void write() {
System.out.println("我寻了半生的春天,你一笑便是了。");
count++;
}
}
Класс Chenmo может точно подсчитать количество строк в однопоточной среде, но не в многопоточной. Из-за операции приращенияcount++
Его можно разделить на три операции: чтение счетчика, увеличение счетчика на 1 и присвоение результата счетчику. При многопоточности синхронизация этих трех операций может быть хаотичной, и конечное значение счетчика будет меньше ожидаемого значения.
PS: Конкретные причины можно рассмотреть в предыдущем разделе "Параллельное программирование на Java (1): подготовка》.
Писать нелегко, мы не можем относиться к Сайленту, не так ли? Тогда придумай что-нибудь.
Предполагая, что поток A изменяет переменную count, необходимо запретить потоку B или потоку C использовать эту переменную, чтобы гарантировать, что поток B или поток C находится в модифицированном состоянии потока A при использовании count.
Как это предотвратить? допустимыйwrite()
добавить методsynchronized
ключевые слова. Пример кода выглядит следующим образом.
class Chenmo {
private long count = 0;
public synchronized void write() {
System.out.println("我寻了半生的春天,你一笑便是了。");
count++;
}
}
ключевые словаsynchronized
Это простейший механизм синхронизации, который гарантирует, что только один поток может выполняться одновременно.write()
, что гарантируетcount++
Безопасен в многопоточной среде.
При написании параллельных приложений мы должны поддерживать правильный настрой, то есть — сначала следить за тем, чтобы код работал правильно, а затем улучшать производительность кода.
Но, как мы все знаем,synchronized
дорогой доступ между несколькими потокамиwrite()
Этот метод является взаимоисключающим. Когда поток B осуществляет доступ, он должен дождаться окончания доступа потока A, что не может отражать основную ценность многопоточности.
java.util.concurrent.atomic.AtomicInteger
— это класс Integer, предоставляющий атомарные операции, а предоставляемые им операции сложения и вычитания являются потокобезопасными. Таким образом, мы можем изменить класс Chenmo следующим образом. Пример кода выглядит следующим образом.
class Chenmo {
private AtomicInteger count = new AtomicInteger(0);
public void write() {
System.out.println("我寻了半生的春天,你一笑便是了。");
count.incrementAndGet();
}
}
write()
метод больше не нуженsynchronized
Ключевые слова остаются синхронизированными, поэтому больше нет необходимости вызывать этот метод взаимоисключающим образом между несколькими потоками, что может в определенной степени повысить эффективность статистики.
Однажды формат статистики сборов за публикацию изменился не только в количестве строк, но и в количестве слов, поэтому классу Chenmo необходимо добавить еще одну переменную-член. Пример кода выглядит следующим образом.
class Chenmo {
private AtomicInteger lineCount = new AtomicInteger(0);
private AtomicInteger wordCount = new AtomicInteger(0);
public void write() {
String words = "我这一辈子,走过许多地方的路,行过许多地方的桥,看过许多次的云,喝过许多种类的酒,却只爱过一个正当年龄的人。";
System.out.println(words);
lineCount.incrementAndGet();
wordCount.addAndGet(words.length());
}
}
Как вы думаете, этот код является потокобезопасным?
Оказывается, этот код не является потокобезопасным. Поскольку lineCount и wordCount являются двумя переменными, хотя они потокобезопасны, когда поток A добавляет 1 к lineCount, нельзя гарантировать, что поток B начнет добавлять 1 к lineCount после того, как поток A завершит подсчет wordCount.
Что мы можем сделать по этому поводу? Способ тоже очень простой, пример кода следующий.
class Chenmo {
private int lineCount = 0;
private int wordCount = 0;
public void write() {
String words = "我这一辈子,走过许多地方的路,行过许多地方的桥,看过许多次的云,喝过许多种类的酒,却只爱过一个正当年龄的人。";
System.out.println(words);
synchronized (this) {
lineCount++;
wordCount++;
}
}
}
Заблокируйте код подсчета строк (lineCount++) и подсчета слов (wordCount++), чтобы эти две строки кода были атомарными. Другими словами, когда поток B выполняет статистику, он должен дождаться завершения сбора статистики потоком A перед запуском.
synchronized (lock) {...}
Это простой встроенный механизм блокировки, предоставляемый Java для обеспечения атомарности блоков кода. Поток автоматически получает блокировку перед входом в заблокированный блок кода и снимает блокировку при выходе из блока кода, гарантируя, что набор операторов выполняется как неделимая единица.
Предыдущий:Параллельное программирование на Java (1): введение
Следующий:Как гарантировать атомарность общих переменных?
Поиск в WeChat »Тихий король 2"Общественный номер, подпишитесь и ответьте"бесплатное видео"Получите 500 ГБ высококачественных обучающих видео (по категориям).