яЯ программирую на Java уже 11 лет, определенно ветеран, но для параллельного программирования на JavaяПросто новичок. Я, вероятно, буду осмеян некоторыми мастерами за то, что скажу это, но я не боюсь.
Потому что я знаю, что каждый год появляется много-много новых людей, желающих присоединиться к армии программирования на Java, и они будут чувствовать себя беспомощными перед проблемами, возникающими при "параллельном" программировании. И я хотел бы присоединиться к ним в новом раунде изучения основ параллельной разработки программ с использованием потоков Java.
01. Почему мы должны изучать параллелизм?
Мой мозг не был просветлен Татхагатой Буддой, поэтому мне нравится думать об одной вещи за другой, и я не могу использовать принцип «один мозг и два применения». Но некоторые боссы отличаются, например Чжугэ Лян, который может говорить и играть на пианино, думая о партитуре для фортепиано, а также может понять план Сыма И после отступления из армии.
У босса Чжугэ есть суперспособность «параллельности». Если бы это был я, столкнувшийся с армией Сыма И, состоящей из десятков миллионов человек, я не только не мог бы играть на пианино, но и был бы напуган до глубины души.
У всех только один мозг, как у компьютера только один процессор. Но мозг не означает, что он не может быть «двойного назначения», ключ в том, есть ли у мозга способность «одновременно».
Если у мозга есть способность к параллелизму, он действительно достаточно силен, чтобы летать, подумайте о появлении Сыма И, напуганного спокойным боссом Чжугэ.
Для программ, если они имеют возможность параллелизма, эффективность может быть значительно повышена. Вы, должно быть, зарегистрировали много веб-сайтов и получили много кодов подтверждения.Если на стороне сервера веб-сайта не настроен выделенный поток для обработки (одновременно) при отправке кода подтверждения, если сеть не работает и блокировка происходит, то на стороне сервера Вам не нужно ждать от рассвета до темноты, чтобы узнать, получили ли вы код подтверждения? Хорошо, если это только вы, но что, если пользователей будет сотня? Неужели эти 100 пользователей должны сдуру там ждать?
Можно себе представить, насколько важно параллельное программирование! Более того, знание того, следует ли понимать виртуальную машину Java и следует ли программировать одновременно, является почти единственным правилом для определения того, является ли Java-разработчик мастером. такЕсли вы хотите зарабатывать больше, вы должны быть одновременными.!
02. Первый шаг параллелизма — создать поток
Обычно запуск программы эквивалентен запуску процесса. На каждом компьютере запущено множество программ, поэтому в диспетчере процессов вы увидите множество процессов. Вы скажете, это не ерунда?
Нет-нет-нет, долгое время, когда я только учился программировать, я предполагал, что эти процессы — потоки, но потом узнал, что это не так. В процессе может быть запущено много потоков или может быть только один.
Основная функция на самом деле является основным потоком. Мы можем создать множество других потоков внутри этого основного потока. Взгляните на код ниже.
public class Wanger {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我叫" + Thread.currentThread().getName() + ",我超喜欢沉默王二的写作风格");
}
});
t.start();
}
}
}
Самый распространенный способ создать поток — объявить реализациюRunnable
Анонимный внутренний класс для интерфейса; затем создайте его какThread
параметры объекта, затем вызовитеThread
объектstart()
способ начать. Результат бега следующий.
我叫Thread-1,我超喜欢沉默王二的写作风格
我叫Thread-3,我超喜欢沉默王二的写作风格
我叫Thread-2,我超喜欢沉默王二的写作风格
我叫Thread-0,我超喜欢沉默王二的写作风格
我叫Thread-5,我超喜欢沉默王二的写作风格
我叫Thread-4,我超喜欢沉默王二的写作风格
我叫Thread-6,我超喜欢沉默王二的写作风格
我叫Thread-7,我超喜欢沉默王二的写作风格
我叫Thread-8,我超喜欢沉默王二的写作风格
我叫Thread-9,我超喜欢沉默王二的写作风格
Из бегущих результатов видно, что порядок выполнения потоков не от 0 до 9, а имеет определенную случайность. Это связано с тем, что параллелизм Java является упреждающим,Хотя нить 0 была создана самой ранней, ее способность «конкурировать за благосклонность» посредственна, и находиться на верхней позиции труднее..
03. Второй шаг параллелизма — создание пула потоков
java.util.concurrent.Executors
Класс предоставляет ряд фабричных методов для создания пулов потоков, которые могут группировать несколько потоков вместе для более эффективного управления. Примеры следующие.
public class Wanger {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("我叫" + Thread.currentThread().getName() + ",我超喜欢沉默王二的写作风格");
}
};
executorService.execute(r);
}
executorService.shutdown();
}
}
Результат бега следующий.
我叫pool-1-thread-2,我超喜欢沉默王二的写作风格
我叫pool-1-thread-4,我超喜欢沉默王二的写作风格
我叫pool-1-thread-5,我超喜欢沉默王二的写作风格
我叫pool-1-thread-3,我超喜欢沉默王二的写作风格
我叫pool-1-thread-4,我超喜欢沉默王二的写作风格
我叫pool-1-thread-1,我超喜欢沉默王二的写作风格
我叫pool-1-thread-7,我超喜欢沉默王二的写作风格
我叫pool-1-thread-6,我超喜欢沉默王二的写作风格
我叫pool-1-thread-5,我超喜欢沉默王二的写作风格
我叫pool-1-thread-6,我超喜欢沉默王二的写作风格
Executors
изnewCachedThreadPool()
Метод используется для создания кэшируемого пула потоков и вызова метода пула потоков.execute()
Предыдущий поток можно использовать повторно, пока этот поток доступен; например,pool-1-thread-4
,pool-1-thread-5
иpool-1-thread-6
возможность повторного использования. Лучший представитель имиджа, о котором я могу думать, это императрица У Цзэтянь.
Если потоки недоступны, новый поток создается и добавляется в пул. Конечно, потоки, которые не использовались в течение 60 секунд, также удаляются из кеша.
Кроме того,Executors
изnewFiexedThreadPool(int num)
Метод используется для создания пула потоков с фиксированным количеством потоков;newSingleThreadExecutor()
метод используется для создания однопоточного пула потоков (вы можете придумать ситуацию, в которой его следует использовать?).
Тем не менее, история вот-вот повернется. В руководстве по разработке Java от Alibaba (которое можно получить, ответив на ключевое слово «Java» на фоне официальной учетной записи «Silent King II») четко указано, чтоне допускаетсяИспользуйте Executors для создания пулов потоков.
Не работаетExecutors
Создайте пул потоков, а затем как создать пул потоков?
позвонить напрямуюThreadPoolExecutor
Конструктор для создания пула потоков. фактическиExecutors
Это то, что он делает, но это неправильноBlockQueue
Укажите емкость. Все, что нам нужно сделать, это указать емкость во время создания. Пример кода выглядит следующим образом.
ExecutorService executor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));
04. Третий шаг параллелизма — решить проблему конкуренции за общие ресурсы.
Однажды, когда я с семьей делал покупки в торговом центре, когда я вышел из лифта, в лифт вбежал какой-то идиот. Коляска моей дочери была прижата к ногам идиота, а он тыкал мне в нос и кричал. Я ударил его прямо в нос, и мы запутались.
Что показывает это дело? Во-первых, очень хлопотно встретить идиотов, которые не говорят о цивилизации и не знают правила «первым вышел, последним пришел» (LIFO); во-вторых, в борьбе за общие ресурсы приходится воевать друг с другом.
В Java первым решением проблемы конкуренции за общие ресурсы является использование ключевых слов.synchronized
. Когда выполнение потокаsynchronized
При защите фрагмента кода этот код будет заблокирован, а другие потоки, вызывающие этот код, будут заблокированы до снятия блокировки.
Следующий код используетThreadPoolExecutor
Создается пул потоков, и каждый поток в пуле добавляет 1 к счетчику общих ресурсов. А теперь закройте глаза и подумайте, каким будет значение count, когда завершится выполнение 1000 потоков?
public class Wanger {
public static int count = 0;
public static int getCount() {
return count;
}
public static void addCount() {
count++;
}
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(10, 1000,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10));
for (int i = 0; i < 1000; i++) {
Runnable r = new Runnable() {
@Override
public void run() {
Wanger.addCount();
}
};
executorService.execute(r);
}
executorService.shutdown();
System.out.println(Wanger.count);
}
}
На самом деле значение счетчика общих ресурсов, скорее всего, будет 996, 998, но редко 1000. Зачем?
Потому что пока один поток записывает в эту переменную, другой поток может читать эту переменную или записывать в эту переменную. Эта переменная становится «неопределенным состоянием» данных.Эта переменная должна быть защищена.
Обычной практикой является изменение этой переменнойaddCount()
добавить методsynchronized
Ключевое слово — гарантирует, что потоки ставятся в очередь по порядку при доступе к этой переменной.
Пример выглядит следующим образом:
public synchronized static void addCount() {
count++;
}
Есть еще один распространенный метод — блокировки чтения-записи. Подразделяется на блокировку чтения и блокировку записи.Множественные блокировки чтения не являются взаимоисключающими.Блокировки чтения и блокировки записи являются взаимоисключающими и контролируются виртуальной машиной Java. Если код позволяет нескольким потокам читать одновременно, но не может одновременно записывать, применяется блокировка чтения; если код не допускает одновременного чтения и только один поток выполняет запись, применяется блокировка записи.
Интерфейс блокировки чтения-записиReadWriteLock
, конкретный класс реализацииReentrantReadWriteLock
.synchronized
Он принадлежит мьютексу, операции чтения и записи разрешены только одному потоку в любое время, а другие потоки должны ждать; иReadWriteLock
Множественным потокам разрешено получать блокировки чтения, но только одному потоку разрешено получать блокировки записи, что относительно эффективно.
Сначала мы создаем синглтон для блокировки чтения-записи, используя перечисление. код показывает, как показано ниже:
public enum Locker {
INSTANCE;
private static final ReadWriteLock lock = new ReentrantReadWriteLock();
public Lock writeLock() {
return lock.writeLock();
}
}
сноваaddCount()
способcount++;
заблокирован. Примеры следующие.
public static void addCount() {
// 上锁
Lock writeLock = Locker.INSTANCE.writeLock();
writeLock.lock();
count++;
// 释放锁
writeLock.unlock();
}
При использовании блокировки чтения-записи не забудьте снять блокировку в конце.
05. Наконец
Сложно ли научиться параллельному программированию? Если честно, это действительно непросто. Взгляните на карту ума, составленную г-ном Ван Баолином, чтобы узнать.
Но вы также знаете, "Замороженные три фута - это не дневной холод", обучение - это шаг за шагом. Пока вы научитесь создавать поток, узнаете, как создать пул потоков и научитесь решать проблему конкуренции за общие ресурсы, вы уже сделали большой шаг в области параллельного программирования шаг.
Зарядись топливом, ладно?
Предыдущий:Начало работы с вводом/выводом Java
Следующий:Параллельное программирование на Java (1): введение
Поиск в WeChat »Тихий Король Три"Общественный номер, подпишитесь и ответьте"бесплатное видео"Получите 500 ГБ высококачественных обучающих видео (по категориям).