1 Что является проблемой параллелизма.
Несколько процессов или потоков, получающих доступ к одному и тому же ресурсу одновременно (или в тот же период времени), вызовут проблемы с параллелизмом.
Типичным примером является то, что два банка работают одновременно одновременно. Например, операторы A и B прочитают учетную запись с балансом 1000 юаней. Оператор A добавляет 100 юаней к учетной записи, оператор B также снижает 50 юаней для учетной записи, сначала подчиняется, а B подчиняется позже. Окончательный фактический баланс счета составляет 1000-50 = 950 юаней, но он должен был быть 1000+100-50 = 1050. Это типичная проблема параллелизма. Как это решить? Может использовать замки.
2 Usage of Synchronized в Java
Использование 1
Общественный тест класса {public synchronized void print () {….;}}Если поток выполняет метод print (), объект будет заблокирован. Другие потоки не смогут выполнить все синхронизированные блоки объекта.
Использование 2
Общедоступный тест класса {public void print () {synchronized (this) {// блокировать этот объект…;}}} То же использование 1, но оно может лучше отражать сущность синхронизированного использования.
Использование 3
Общедоступный тест класса {частная строка a = "test"; public void print () {synchronized (a) {// блокировать объект ...;}} public synchronized void t () {…; // Этот блок синхронизированного кода не будет заблокирован из -за print ().}}}}}}}}}}}Выполнение print () будет блокировать объект a. Обратите внимание, что он не блокирует объект теста, что означает, что другие синхронизированные методы испытательного объекта не будут заблокированы из -за Print (). После того, как код синхронизации выполняется, заблокирован для A.
Чтобы заблокировать кодовый блок объекта, не влияя на высокопроизводительное написание других синхронизированных блоков объекта:
Общедоступный тест класса {private byte [] lock = new Byte [0]; public void print () {synchronized (lock) {…;}} public synchronized void t () {…;}}Статический метод блокировка
Общедоступный тест класса {public static void execute () {…;}}Тот же эффект
Общественный тест класса {public static void execute () {synchronized (testthread.class) {…;}}}3 замка в Java и очереди вверх, чтобы пойти в туалет.
Замок - это способ предотвратить доступ к другим процессам или потокам, то есть заблокированный ресурс не может быть доступен по другим запросам. В Java ключевое слово Sychronized используется для блокировки объекта. например:
открытый класс mystack {int idx = 0; char [] data = new char [6]; public void push (char c) {data [idx] = c; idx ++;} public char pop () {idx-; return data [idx];} public static void main (string args) {myStack m = newSacked (). Строго говоря, все синхронизированные блоки объекта M заблокированы. Если есть еще один поток T, пытающийся получить доступ к M, то T не может выполнить методы push и pop объекта M. */m.pop (); // объект M заблокирован. }}Заблокировка Java точно совпадает с несколькими людьми, которые стоят в очереди на общественный туалет. Первый человек запер дверь изнутри после входа, поэтому другим пришлось ждать в очереди. Дверь откроется (разблокирован) только тогда, когда первый человек выйдет после конца. Настала очередь второго человека, чтобы войти, и он снова заблокировал дверь изнутри, а другие будут продолжать ждать в очереди.
Легко понять, используя теорию туалета: когда человек входит в туалет, туалет будет заперт, но это не приведет к заперту другого туалета, потому что один человек не может приседать в двух туалетах одновременно. Для Java это означает: замки в Java предназначены для одного и того же объекта, а не на классе. См. Следующий пример:
Mystatckm1 = newmystack (); mystatckm2 = newmystatck (); m1.pop (); m2.pop ();
Замки объектов M1 не повлияют на замки M2, потому что они не одинаковые положения туалета. То есть, если есть 3 потока T1, T2 и T3, эксплуатирующие M1, то эти 3 потока могут только в очереди и ждать M1. Предполагая, что остальные 2 потока T8, T9, работающий M2, то T8, T9 будет ждать только на M2. T2 и T8 не имеют к этому никакого отношения. Даже если замок на M2 выпущен, T1, T2 и T3, возможно, все равно придется стоять в очереди на M1. Там нет причин, это не то же сиденье туалета.
Java не может одновременно добавить два замка в кодовый блок. Это отличается от механизма блокировки базы данных. База данных может одновременно добавить несколько разных блокировков в запись.
4 Когда я должен выпустить замок?
Как правило, блокировка выпускается после выполнения блока кода синхронизации (блок заблокированного кода), или блокировка может быть выпущена на полпути с помощью метода wait (). Метод wait () похож на приседание в туалете на полпути, и вдруг я обнаружил, что канализация была заблокирована. Мне пришлось выйти и встать, чтобы ремонтник канализации (поток, готовясь к выполнению уведомления), мог проникнуть и очистить туалет. После разблокировки мастер кричал: «Он был отремонтирован». Товарищ, который вышел только сейчас, снова выстроился в очередь, услышав это. Обратите внимание, вы должны подождать, пока мастер выйдет. Если Мастер не выйдет, никто не может войти. То есть после уведомления, другие потоки могут войти в заблокированную область и немедленно перемещаться, но другие потоки могут входить после заблокированной области, где код уведомления выполняется и выпущен для освобождения блокировки.
Вот пример кода ожидания и уведомления:
Общедоступный синхронизированный char pop () {char c; while (buffer.size () == 0) {try {this.wait (); // out из туалета} catch (прерывание Exception e) {// Игнорировать его…}} c = ((символ) buffer.remove (buffer.size ()-1)). charvalue (); return c;} public synchronized void push (char c) {this.notify (); // уведомить эти потоки wait (), чтобы снова встать в очередь. ПРИМЕЧАНИЕ. Просто уведомите их о повторном Queckule. Символ charobj = новый символ (c); buffer.addelement (charobj);} // Выполнить и отпустить блокировку. Эти потоки в очереди могут войти.Иди глубже.
Из -за операции WAIT () товарищи, выходящие на полпути, не будут стоять в очереди, пока не получили сигнал уведомления. Он будет наблюдать, как люди очередися рядом с ним (среди них также есть мастер ремонта воды). Обратите внимание, что мастер ремонта водопроводной трубы не может пойти на линию, и он должен стоять в очереди, как те, кто идет в туалет. Дело не в том, что после того, как человек приседает на полпути, мастер ремонта водопроводной трубы может внезапно появиться и выйти, чтобы немедленно починить его. Он хочет справедливо конкурировать с людьми, которые изначально стояли в очереди, потому что он также является обычной веткой. Если магистр ремонта водопроводной трубы выровнен, человек впереди обнаружит, что он заблокирован и подождите, затем выйдите и встаньте в сторону, подождите, встаньте, встаньте в сторону и пойдите только к мастеру, чтобы войти и выполнить уведомление. Таким образом, через некоторое время рядом с очередью будет куча людей, ожидающих уведомления.
Наконец, Мастер вошел и уведомил. Что дальше?
1. Ожидающий человек (поток) уведомляется.
2. Почему он уведомил вместо другого ожидания? Зависит от JVM. Мы не можем превентивно
Определите, какой из них будет уведомлен. Другими словами, те, у кого высокий приоритет, могут не разбудиться в первую очередь, ожидая
В любое время не обязательно пробуждается в первую очередь, все непредсказуемо! (Конечно, если вы знаете JVM
При реализации вы можете предсказать это).
3. Он (уведомленная нить) должен снова вставать в очередь.
4. Будет ли он в первую очередь в линии? Ответ: не уверен. Он будет последним? Не обязательно.
Но если приоритет потока будет относительно высоким, то вероятность того, что он ранжирован сначала, относительно высока.
5. Когда настанет его очередь вернуться в туалетное сиденье, он будет продолжать выполнять из последнего места wat () и не будет повторно выполнять.
Чтобы выразить это отвратительным способом, он будет продолжать бродящий, а не бродя снова.
6. Если мастер уведомлений (), то все люди, которые сдались на полпути, снова выстроятся в очередь. Порядок неизвестен.
Javadoc говорит, что TheaWakenedThreadswillnotbeabletoProecdetUntilTheCurrentThreadRelinquishthelockonthiSobject (пробужденный поток не может быть выполнен до того, как текущий поток выпустит блокировку).
Это очевидно, чтобы объяснить, используя теорию сидений туалета.
5лок. Использование
Используйте синхронизированное ключевое слово для блокировки ресурсов. Ключевое слово блокировки тоже в порядке. Это новое в JDK1.5. Использование выглядит следующим образом:
Class BroundedBuffer {final Lock Lock = new ReentrantLock (); Final Condity natufull = lock.newCondition (); Final Condity Notempty = lock.newCondition (); Final Object [] items = new Object [100]; int putpt natufull.await (); items [putptr] = x; if (++ putptr == eption.length) putptr = 0; ++ count; notempty.signal ();} наконец {lock.unlock ();}} public obste () throws urpturexexcept {lock.lock (); {whence = when not not not not not -alptrat. (++ tableptr == items.length) tableptr = 0;-count; natufull.signal (); return x;} наконец {lock.unlock ();}}}(Примечание: это пример в Javadoc, пример реализации блокирующей очереди. Так называемая блокирующая очередь означает, что если очередь полна или пуста, она вызовет блокировку и ожидание резьбы. Аррейблекска в Java обеспечивает готовую к блокирующую долю, и вам не нужно писать один из них конкретно.).
Код между lock.lock () и lock.unlock () объекта будет заблокирован. Что лучше в этом методе по сравнению с синхронизацией? Короче говоря, он классифицирует потоки ожидания. Чтобы описать это, используя теорию сидений туалета, у тех, кто приседает на полпути и выходит из сиденья туалета и подождать, могут иметь разные причины. Некоторые из них связаны с тем, что туалет заблокирован, а некоторые - потому, что туалет не в воде. При уведомлении вы можете кричать: если вы ждете блокировки туалета, вы вернетесь в очередь (например, проблема заблокированного туалета была решена) или кричать, если вы ждете блокировки туалета, вы вернетесь в линию (например, проблема туалета будет решена). Это позволяет получить более подробный контроль. В отличие от ожидания и уведомления в синхронизации, независимо от того, является ли туалет заблокирован или туалет не водянистый, вы можете только кричать: я просто ждал здесь, чтобы стоять в очереди! Если люди в очереди входят и смотрят, они обнаруживают, что проблема туалета просто решается, и проблема, которую они стремятся решить (туалет нет воды), еще не решен, поэтому им нужно вернуться и подождать (подождите) и приходить в тщетном ходу, тратя время и ресурсы.
Соответствующая связь между методом блокировки и синхронизированной:
Lockawaitsignalsignalall
SynchronizedWaitNotifyNotifyall
Примечание. Не звоните, ожидание, уведомление, уведомление в блоках, заблокированных за блокировкой
6. Используйте трубопроводы для общения между потоками
Принцип прост. Две потоки, одна управляет PipedInputStream, а другая управляет PipedOutputStream. Данные, записанные PipedOutputStream, сначала кэшируются в буфере. Если буфер заполнен, этот поток ждет. PipedInputStream считывает данные в буфере. Если у буфера нет данных, этот поток ждет.
Та же функция может быть достигнута путем блокировки очередей в JDK1.5.
Пакет io; импортировать java.io.*; public class PipedStreamTest {public static void main (string [] args) {pipedOutputStream Ops = new PipedOutputStream (); PipEdinPutStream pis = new PipedInputStream (); try {ops.connect (pis); // реализовать New Producer (Ops). Consumer (pis) .run ();} Catch (Exception e) {e.printStackTrace ();}}} // Производитель продюсера реализует Runnable {pribleToutputStream Ops; public Produce (PipedOutputStream Ops) {this.Ops = ops;} public void; run () {try {ops.write ("hell, spell" .getbytes ()); ops.close ();} catch (excection e) {e.printstacktrace ();}}} // потребительский класс. Потребитель реализует бегство {pipedinputstream pis; public Consumer (pipedinptream pis) {this.pis = pis; run () {try {byte [] bu = new Byte [100]; int len = pis.read (bu); system.out.println (new String (bu, 0, len)); pis.close ();} catch (Exception e) {e.printStackTrace ();}}}Пример 2: Небольшое изменение вышеуказанной программы становится двумя потоками.
Пакет io; импортировать java.io.*; public class pipedstreamtest {public static void main (string [] args) {PipedOutputStream Ops = new PipedOutputStream (); PipedInputStream pis = new PipedInputStream (); try {ops.conce (pis);//реализация Pipeline Connection Produce p = new Produce (Ops); Thread (p) .start (); Consumer C = новый потребитель (pis); new Thread (c) .start ();} catch (Exception e) {e.printstacktrace ();}}}} // Производитель продюсер внедряет {pripaudoutput -ops; publicater (PipedOutpream ops) {this.ops = aods = aoDs = run () {try {for (;;) {ops.write ("hell, spell" .getbytes ()); ops.close ();}}} // потребительский класс. Потребители реализуют бегнущие {pripedinptream pis; public Conmercer (pipedinputstream pis) {this.pis = pis;} public void run () {try {) {try {) {try {) {try {) {{) {) {try.) bu = new Byte [100]; int len = pis.read (bu); system.out.println (new String (bu, 0, len));} pis.close ();} catch (Exception e) {e.printstacktrace ();}}}Пример 3. Этот пример более подходит для приложения
Импорт java.io.*; открытый класс Pipedio {// После запуска программы скопируют содержимое файла sendfile в файл приемвета public static void main (String args []) {try {// Создание объекта считывания и записи PipedInptream pis = new PipedInpream ();//pipedOutputput -stuctrempream hipedIpstream ();//pipeductream (). pos.connect (pis); // Создание двух потоков и запуск. Новый отправитель (POS, "c:/text2.txt"). start (); new Receiver (pis, "c:/text3.txt"). start ();} catch (ioexception e) {system.out.println ("Ошибка труб" + e);}}}} // потоковой сигнал Sender Sender Extends {PipedOutputem SendEm SendEm SendEmeAm SendEMEAM SendEMEAM SENDEMEAM; POS, String FileName) {this.pos = pos; file = new File (filename);} // метод запуска потока public void run () {try {// чтение содержимого файла файла fileInptream fs = new FileInputStream (file); int data; while ((data = fs.read ())! pos.write (data);} pos.close ();} catch (ioexception e) {System.out.println ("Ошибка отправителя" +e);}}}} // Класс Tread Class Extends {PipedInputStream Pis; // method Construction (PipedInputStream PIS, String FILENAME) {this.pis = pist); File (fileName);} // Поток запускает public void run () {try {// write file-поток объект fileOutputStream fs = new FileOutputStream (file); int data; // Читать while ((data = pis.read ())! =-1) {// записать в локальный файл fs.write (data);} pis.clase (); e) {System.out.println ("Ошибка приемника" +e);}}}7 Блокировка очереди
Очередь блокировки может заменить метод потока трубопровода для реализации режима впускной/дренажной трубы (производитель/потребитель). JDK1.5 предоставляет несколько готовых очередей блокировки. Теперь давайте посмотрим на код ArrayBlockingQueue следующим образом:
Вот блокировка очереди
BlockingQueue Blockingq = новый ArrayBlockingqueue 10;
Поток берет из очереди
for (;;) {object o = blockingq.take (); // очередь пуста, затем подождите (блокировка)}Другая ветка хранится в очереди
for (;;) {blockingq.put (new Object ()); // Если очередь полна, подождите (блокировка)}Можно видеть, что блокирующие очереди проще в использовании, чем трубопроводы.
8 ИСПЫТАНИЕ исполнителей, исполнители, исполнители, ThreadPoolexeCutor
Вы можете использовать задачи по управлению потоками. Вы также можете использовать набор классов, предоставленных JDK1.5 для более удобных задач. Из этих категорий мы можем испытать ориентированный на задачу образ мышления. Эти классы:
Интерфейс исполнителя. Как использовать:
Исполнитель Исполнитель = anexeCutor; // генерировать экземпляр исполнителя. Исполнитель.execute (new RunnableTask1 ());
Намерение: пользователи сосредоточены только на выполнении задач, и не нужно беспокоиться о создании и выполнении задач, а также о других вопросах, о которых обеспокоены сторонними исполнителями. То есть отделите выполнение вызова задачи и реализацию задачи.
На самом деле, уже есть отличные реализации этого интерфейса в JDK1.5. Достаточно.
Исполнители - это фабричный класс или класс инструментов, такие как коллекции, используемые для генерации экземпляров различных интерфейсов.
Интерфейс executorservice унаследован от Executor.executor просто бросает задачу в Executor () для выполнения и игнорирует остальные. Исполнители отличается, он будет выполнять больше контроля. например:
Class NetworkService {Private Final Serversocket Serversocketcocket; Private Final ExecutorService Pool; Public NetworkService (int port, int poolsize) Throws ioexception {serversocket = new Serversocket (port); pool = executors.newfixedthreadpool (poolsize);} public void sera Handler (serversocket.accep ()));}} catch (ioexception ex) {pool.shutdown (); // Нет новых задач не выполняются}}} Классовые обработчики реализуют {private final сокет;После того, как Executorservice (то есть объект Pool в коде) выполняет выключение, он больше не может выполнять новые задачи, но старые задачи будут продолжать выполняться, и те, кто ожидает выполнения, больше не будут ждать.
Подача задачи и общение исполнителя
public static void main (string args []) throws exception {executorservice rececutor = executors.newsingleThreadExeCutor (); callable task = new Callable () {public String call () Throws Exception "return" test ";}}; будущий f = executor. System.out.println (result); executor.shutdown ();}Экземпляр исполнителя, полученный executors.newsingleThreadExeCutor () имеет следующие характеристики:
Выполнение задачи последовательно. Например:
Исполнитель.submit (task1); Исполнитель.submit (task2);
Вы должны ждать выполнения Task1 до выполнения Task2.
Task1 и Task2 будут помещены в очередь и обработаны рабочим потоком. То есть: всего 2 потока (основной поток, рабочий поток, который обрабатывает задачи).
Для других занятий, пожалуйста, обратитесь к Javadoc
9 Параллельное управление процессом
Примеры в этом разделе взяты из учебного пособия Wen Shao по параллелизму Java и могут быть изменены. Салют мистеру Вэну.
Countdownlatch Door Pin Pin
Начните ветку и подождите, пока поток заканчивается. То есть общий основной поток и другие детские потоки выполняются после окончания основного потока.
public static void main (string [] args) выбрасывает исключение {// todo автоматическое сгенерированное метод, окончательный count = 10; final countdownlatch explatch = new countdownlatch (count); // Определить количество защелок дверей-10 для (int i = 0; i <count; i ++) {thread = new Thread ("Worker Thread"+i) {public run run () {) {public void. olldElatch.countdown (); // уменьшить одну дверную защелку}}; thread.start ();} olldElatch.await (); // Если защелка дверей еще не была уменьшена, подождите. }В JDK1.4 общий метод состоит в том, чтобы установить статус детского потока и обнаружение цикла основного потока. Простота использования и эффективность не очень хороши.
Начните много тем и подождите, пока уведомления запустите
public static void main (string [] args) бросает исключение {// todo автоматическое сгенерированное метод, окончательный отсчет, startlatch = new countdownlatch (1); // определить защелку двери для (int i = 0; i <10; i ++) {Thread Thread = new Thread ("Worker Thread"+I) void run () {trestlatch. Еще не уменьшено, wait} catch (прерывание Exception e) {} // do xxxx}}; thread.start ();} startlatch.countdown (); // уменьшить одну дверную защелку}Cyclibarrier. Только после того, как все потоки достигают стартовой линии, они могут продолжать работать.
Общедоступный класс Cyclibarriertest реализует runnable {Private Cyclibarrier Barrier; Public Cyclibarriertest (Cyclibarrier Barrier) {this.barrier = barrier;} public void run () {// do xxxx; try {this.barrier.await (); // Поток будет проверять, будут ли прибыли все другие потоки. Если вы не приедете, продолжайте ждать. Когда все достигнуто, выполните содержимое корпуса функции выполнения барьерной работы} Catch (Exception e) {}}/** * @param args */public static void main (string [] args) {// Параметр 2 означает, что оба потока достигли начальной линии до того, как они начнут выполнять вместе Cyclicbarrier = new Cyclier (2, newranable () publicaid upitaid () publicaid upitaid () vublice runable () publicaid () publicaid () vublicaine runaid () upitable () upitable () upitable () upitable (). xxxx;}}); Thread T1 = новый поток (новый CyclibarrierTest (barrier)); Thread T2 = новый поток (новый Cyclibarriertest (barrier)); t1.start (); t2.start ();}}Это упрощает традиционный способ реализации этой функции с помощью счетчика + wat/natifyall.
10 Совпад 3 Закон
Закон Амдала. Учитывая шкалу проблемы, параллелизационная часть составляет 12%. Затем, даже если параллелизм применяется к крайности, производительность системы может быть улучшена только на 1/(1-0,12) = 1,136 раза больше всего. То есть: параллелизм имеет верхний предел для улучшения производительности системы.
Закон Густафсона. Закон Густафсона говорит, что закон Амдаля не считает, что больше вычислительной мощности может использоваться по мере увеличения количества процессоров. Суть состоит в том, чтобы изменить масштаб проблемы, чтобы оставшиеся 88% серийной обработки в законе Амдаля могли быть параллелизированы, прорывая порог производительности. По сути, это своего рода время обмена космическим обменом.
Солнечный закон. Это дальнейшее продвижение первых двух законов. Основная идея заключается в том, что скорость вычислений ограничена скоростью хранения, а не ЦП. Поэтому мы должны в полной мере использовать вычислительные ресурсы, такие как пространство для хранения, и попытаться увеличить масштаб проблемы для создания лучших/более точных решений.
11 от одновременного до параллельного
Компьютеры должны быстро рассчитать при выявлении объектов, чтобы чипы стали горячими и горячими. Когда люди распознают объекты, они ясны с первого взгляда, но они не вызывают сжигания определенной клетки мозга (преувеличен) и чувствуют себя некомфортно. Это потому, что мозг представляет собой распределенную параллельную систему работы. Точно так же, как Google может использовать некоторые дешевые серверы Linux для выполнения огромных и сложных расчетов, бесчисленные нейроны в мозге рассчитывают независимо, делясь результатами друг с другом, и мгновенно завершают эффект, который требует триллионов операций для одного процессора. Представьте себе, что если он будет создан в области параллельной обработки, это окажет неизмеримое влияние на развитие и будущее компьютеров. Конечно, также можно себе представить проблемы: многие проблемы нелегко разделить ».
Суммировать
Выше приведено все содержание этой статьи о проблеме параллелизма Java, и я надеюсь, что это будет полезно для всех. Заинтересованные друзья могут продолжать ссылаться на другие связанные темы на этом сайте. Если есть какие -либо недостатки, пожалуйста, оставьте сообщение, чтобы указать это. Спасибо, друзья, за вашу поддержку на этом сайте!