Java concurrency на практике pdf
Д. Лонг, К. Бастани | Java в облаке. Spring Boot, Spring Cloud, Cloud Foundry (2019) [PDF]
Sierra K., Bates B., Robson E. | OCP: Java SE 8 Programmer II Exam Guide: Exam 1Z0-809 (2018) [PDF/ePub]
Study for the OCP Java SE 8 Programmer II exam using this effective exam preparation guide from Oracle Press. Written by a team of experts, including two developers of the original exam, OCP Java SE 8 Programmer II Exam Guide (Exam 1Z0-809) offers comprehensive coverage of every subject on the test and lays out essential Java programming skills. Throughout, step-by-step exercises; self-tests; and “Exam Watch,” “Inside the Exam,” and “On the Job” sections highlight salient points and aid in learning. In all, the book and accompanying digital content feature more than 350 practice questions that mirror those on the live test in tone, format, and content.
Clearly explains every topic on Exam 1Z0-809, including:
•Declarations, access control, and enums
•Object orientation
•Assertions and exceptions
•Dates, times, locales, and resource bundles
•I/O and NIO
•Generics and collections
•Inner classes
•Lambda expressions and functional interfaces
•Streams
•Threads
•Concurrency
•JDBC
Alan Mycroft, Mario Fusco, Raoul-Gabriel Urma | Modern Java in Action, 2nd Edition (2018) [En] [PDF]
Manning’s bestselling Java 8 book has been revised for Java 9 and Java 10! In Modern Java in Action, readers build on their existing Java language skills with the newest features and techniques.
The release of Java 9 builds on what made Java 8 so exciting. In addition to Java 8’s lambdas and streams, Java 9 adds a host of new features of its own. It includes new library features to support reactive programming, which give users new ways of thinking about programming and writing code that is easier to read and maintain.
Дэвид Гриффитс, Дон Гриффитс. | Head First. Программирование для Android. 2-е изд. (2018) [PDF]
Ян Ф. Дарвин | Android. Сборник рецептов. Задачи и решения для разработчиков приложений. (2018) [PDF]
Эта книга должна помочь сообществу разработчиков Android поделиться знаниями, которые позволят сделать их приложения еще лучше. Все знания, изложенные в этой книге, облегчат разработку приложений для платформы Android.Книга облегчает создание работоспособных приложений для платформы Android с помощью 230 проверенных рецептов. Второе издание содержит рецепты для работы с пользовательскими интерфейсами, мультисенсорными жестами, механизмами определения местоположения, веб-службами и конкретными возможностями устройства, такими как телефон, видеокамера и акселерометр. Вы также научитесь упаковывать свое приложение для магазина Google Play Market.
Книга идеально подходит для разработчиков, владеющими языком Java, основами платформы Android и интерфейса Java SE API. Она содержит рецепты, предоставленные более чем тридцатью разработчиками. Каждый рецепт содержит четкое решение и пример кода, готовый к использованию.
Craig Walls | Spring in Action, 5th Edition (2018) [En] [PDF]
Spring in Action, 5th Edition is the fully updated revision of Manning’s bestselling Spring in Action. This new edition includes all Spring 5.0 updates, along with new examples on reactive programming, Spring WebFlux, and microservices. You’ll also find the latest Spring best practices, including Spring Boot for application setup and configuration.
Christian Bauer, Gavin King, and Gary Gregory | Java Persistence with Hibernate, Second Edition (2015) [En] [PDF]
Java Persistence with Hibernate, Second Edition explores Hibernate by developing an application that ties together hundreds of individual examples. In this revised edition, authors Christian Bauer, Gavin King, and Gary Gregory cover Hibernate 5 in detail with the Java Persistence 2.1 standard (JSR 338). All examples have been updated for the latest Hibernate and Java EE specification versions.
Herbert Schildt | Java: A Beginner’s Guide, Eighth Edition [En] [PDF]
Thoroughly updated for Java Platform Standard Edition 11, this hands-on resource shows, step by step, how to get started programming in Java from the very first chapter. Written by Java guru Herbert Schildt, the book starts with the basics, such as how to create, compile, and run a Java program. From there, you will learn essential Java keywords, syntax, and commands
Marten Deinum | Spring Boot 2 Recipes / (2018) [PDF, ePub] [En]
Solve all your Spring Boot 2 problems using complete and real-world code examples. When you start a new project, you’ll be able to copy the code and configuration files from this book, and then modify them for your needs. This can save you a great deal of work over creating a project from scratch.
Using a problem-solution approach, Spring Boot 2 Recipes quickly introduces you to Pivotal’s Spring Boot 2 micro-framework, then dives into code snippets on how to apply and integrate Spring Boot 2 with the Spring MVC web framework, Spring Web Sockets, and microservices. You’ll also get solutions to common problems with persistence, integrating Spring Boot with batch processing, algorithmic programming via Spring Batch, and much more. Other recipes cover topics such as using and integrating Boot with Spring’s enterprise services, Spring Integration, testing, monitoring and more.
Крис Шефер, Кларенс Хо, Роб Харроп | Spring 4 для профессионалов / (2015) [PDF]
Книга представляет собой многолетний бестселлер, который обновлен с целью отражения функциональных средств, предлагаемых последней версией платформы Spring Framework 4. С выходом 4-го издания эта популярная книга безоговорочно считается наиболее исчерпывающим и авторитетным руководством по Spring. Вы изучите основы и ключевые темы, связанные с платформой Spring. Авторы поделятся с вами собственным реальным опытом в области удаленной обработки, использования Hibernate и работы с EJB. Помимо основ вы научитесь применять Spring Framework для построения разнообразных уровней или частей корпоративного Java-приложения: транзакций, веб-уровня и уровня презентаций, развертывания и многого другого. Многочисленные примеры помогут вам в освоении технологий и приемов, рассмотренных в этой книге, а также в организации их совместной работы. Устойчивая и легковесная платформа Spring Framework с открытым кодом продолжает быть де-факто лидирующей инфраструктурой для разработки корпоративных Java-приложений. Она тесно взаимодействует с другими Java-технологиями подобного рода, такими как Hibernate, Groovy, MyBatis и т.д. Теперь Spring работает также с Java EE и JPA 2.
Chris Schaefer, Clarence Ho, Iuliana Cosmina, Rob Harrop | Pro Spring 5, 5th Edition / (2017) [PDF]
Master Spring basics and core topics, and share the authors’ insights and real–world experiences with remoting, Hibernate, and EJB. Beyond the basics, you’ll learn how to leverage the Spring Framework to build the various tiers and parts of an enterprise Java application: transactions, web and presentation tiers, deployment, and much more. A full sample application allows you to apply many of the technologies and techniques covered in Pro Spring 5 and see how they work together.
This book updates the perennial bestseller with the latest that the new Spring Framework 5 has to offer. Now in its fifth edition, this popular title is by far the most comprehensive and definitive treatment of Spring available. It covers the new functional web framework and interoperability with Java 9.
What You’ll Learn
Discover what’s new in Spring Framework 5
Use the Spring Framework with Java 9
Master data access and transactions
Work with the new functional web framework
Create microservices and other web services
Who This Book Is For
Experienced Java and enterprise Java developers and programmers. Some experience with Spring highly recommended.
Sciore E. | Java Program Design / (2019) [PDF/ePub]
Get a grounding in polymorphism and other fundamental aspects of object-oriented program design and implementation, and learn a subset of design patterns that any practicing Java professional simply must know in today’s job climate.
Java Program Design presents program design principles to help practicing programmers up their game and remain relevant in the face of changing trends and an evolving language. The book enhances the traditional design patterns with Java’s new functional programming features, such as functional interfaces and lambda expressions. The result is a fresh treatment of design patterns that expands their power and applicability, and reflects current best practice.
The book examines some well-designed classes from the Java class library, using them to illustrate the various object-oriented principles and patterns under discussion. Not only does this approach provide good, practical examples, but you will learn useful library classes you might not otherwise know about.
The design of a simplified banking program is introduced in chapter 1 in a non-object-oriented incarnation and the example is carried through all chapters. You can see the object orientation develop as various design principles are progressively applied throughout the book to produce a refined, fully object-oriented version of the program in the final chapter.
What You’ll Learn:
— Create well-designed programs, and identify and improve poorly-designed ones
— Build a professional-level understanding of polymorphism and its use in Java interfaces and class hierarchies
— Apply classic design patterns to Java programming problems while respecting the modern features of the Java language
— Take advantage of classes from the Java library to facilitate the implementation of design patterns in your programs
Who This Book Is For:
Java programmers who are comfortable writing non-object-oriented code and want a guided immersion into the world of object-oriented Java, and intermediate programmers interested in strengthening their foundational knowledge and taking their object-oriented skills to the next level. Even advanced programmers will discover interesting examples and insights in each chapter.
Java Concurrency на практике
Потоки являются фундаментальной частью платформы Java. Многоядерные процессоры – это обыденная реальность, а эффективное использование параллелизма стало необходимым для создания любого высокопроизводительного приложения.
Улучшенная виртуальная машина Java, поддержка высокопроизводительных классов и богатый набор строительных блоков для задач распараллеливания стали в свое время прорывом в разработке параллельных приложений. В «Java Concurrency на практике» сами создатели прорывной технологии объясняют не только принципы работы, но и рассказывают о паттернах проектирования. Легко создать конкурентную программу, которая вроде бы будет работать. Однако разработка, тестирование и отладка многопоточных программ доставляют много проблем. Код перестает работать именно тогда, как это важнее всего: при большой нагрузке.
В «Java Concurrency на практике» вы найдете как теорию, так и конкретные методы создания надежных, масштабируемых и поддерживаемых параллельных приложений. Авторы не предлагают перечень API и механизмов параллелизма, они знакомят с правилами проектирования, паттернами и моделями, которые не зависят от версии Java и на протяжении многих лет остаются актуальными и эффективными.
Здесь можно скачать книгу «Java Concurrency на практике» для ознакомления (бесплатный PDF фрагмент от правообладателя), почитать онлайн или купить полную электронную версию в форматах FB2, PDF, EPUB, TXT, FB3, MOBI.
Java concurrency на практике pdf
Автор: Гетц Брайан, Пайерлс Тим, Блох Джошуа, Боубер Джозеф, Холмс Дэвид, Ли Даг
Год: 2020
Издательство: Питер
ISBN 978-5-4461-1314-9
Страниц: 464
Язык: Русский
Формат: PDF
Размер: 7 Mb
Содержание: Потоки являются фундаментальной частью платформы Java. Многоядерные процессоры — это обыденная реальность, а эффективное использование параллелизма стало необходимым для создания любого высокопроизводительного приложения.
Улучшенная виртуальная машина Java, поддержка высокопроизводительных классов и богатый набор строительных блоков для задач распараллеливания стали в свое время прорывом в разработке параллельных приложений.
В «Java Concurrency на практике» сами создатели прорывной технологии объясняют не только принципы работы, но и рассказывают о паттернах проектирования. Легко создать конкурентную программу, которая вроде бы будет работать. Однако разработка, тестирование и отладка многопоточных программ доставляют много проблем.
Код перестает работать именно тогда, как это важнее всего: при большой нагрузке. В «Java Concurrency на практике» вы найдете как теорию, так и конкретные методы создания надежных, масштабируемых и поддерживаемых параллельных приложений.
Авторы не предлагают перечень API и механизмов параллелизма, они знакомят с правилами проектирования, паттернами и моделями, которые не зависят от версии Java и на протяжении многих лет остаются актуальными и эффективными.
Книга «Java Concurrency на практике»
Отрывок. Потокобезопасность
Возможно, вас удивит, что конкурентное программирование связано с потоками или замками (1) не более, чем гражданское строительство связано с заклепками и двутавровыми балками. Разумеется, строительство мостов требует правильного использования большого количества заклепок и двутавровых балок, и то же самое касается построения конкурентных программ, которое требует правильного использования потоков и замкˆов. Но это всего лишь механизмы — средства достижения цели. Написание потокобезопасного кода — это, по сути, управление доступом к состоянию и, в частности, к совместному (shared) мутируемому состоянию (mutable state).
В целом состояние объекта — это его данные, хранящиеся в переменных состояния (state variables), таких как экземплярные и статические поля или поля из других зависимых объектов. Состояние хеш-массива HashMap частично хранится в самом объекте HashMap, но также и во многих объектах Map.Entry. Состояние объекта включает любые данные, которые могут повлиять на его поведение.
(1) В тексте встречаются термины lock и block, которые часто переводятся одним словом «блокировка», что может подразумевать и объект, и процесс. В английском языке для процесса блокирования как приостановки продвижения есть термин blocking. Под термином lock имеется в виду «замок», «замковый защитный механизм». Во избежание путаницы термин lock переводится, как замок, кроме устоявшихся выражений, где принят перевод «блокировка». Замок — это механизм контроля доступа к данным с целью их защиты. В программировании замки часто используются для того, чтобы несколько программ или программных потоков могли использовать ресурс совместно, например, обращаться к файлу для его обновления на поочередной основе. — Примеч. науч. ред.
К совместной переменной могут обратиться несколько потоков, мутируемая — меняет свое значение. На самом деле мы пытаемся защитить от неконтролируемого конкурентного доступа не код, а данные.
Создание потокобезопасного объекта требует синхронизации для координации доступа к мутируемому состоянию, невыполнение которой может привести к повреждению данных и другим нежелательным последствиям.
Всякий раз, когда более чем один поток обращается к переменной состояния и один из потоков, возможно, в нее пишет, все потоки должны координировать свой доступ к ней с помощью синхронизации. Синхронизацию в Java обеспечивают ключевое слово synchronized, дающее эксклюзивную блокировку, а также волатильные (volatile) и атомарные переменные и явные замки.
Удержитесь от соблазна думать, что существуют ситуации, не требующие синхронизации. Программа может работать и проходить свои тесты, но оставаться неисправной и завершиться аварийно в любой момент.
Если многочисленные потоки обращаются к одной и той же переменной, имеющей мутируемое состояние, без соответствующей синхронизации, то ваша программа неисправна. Существует три способа ее исправить:
Будут или нет многочисленные потоки обращаться к той или иной переменной, узнать сложно. К счастью, объектно‑ориентированные технические решения, которые помогают создавать хорошо организованные и удобные в сопровождении классы — такие как инкапсуляция и сокрытие данных, — также помогают создавать потокобезопасные классы. Чем меньше потоков имеет доступ к определенной переменной, тем проще обеспечить синхронизацию и задать условия, при которых к данной переменной можно обращаться. Язык Java не заставляет вас инкапсулировать состояние — вполне допустимо хранить состояние в публичных полях (даже публичных статических полях) или публиковать ссылку на объект, который в иных случаях является внутренним, — но чем лучше инкапсулировано состояние вашей программы, тем проще сделать вашу программу потокобезопасной и помочь сопроводителям поддерживать ее в таком виде.
При проектировании потокобезопасных классов хорошие объектно‑ориентированные технические решения: инкапсуляция, немутируемость и четкая спецификация инвариантов — будут вашими помощниками.
Если хорошие объектно‑ориентированные проектные технические решения расходятся с потребностями разработчика, стоит поступиться правилами хорошего проектирования ради производительности либо обратной совместимости с устаревшим кодом. Иногда абстракция и инкапсуляция расходятся с производительностью — хотя и не так часто, как считают многие разработчики, — но образцовая практика состоит в том, чтобы сначала делать код правильным, а затем — быстрым. Старайтесь задействовать оптимизацию только в том случае, если измерения производительности и потребности говорят о том, что вы обязаны это сделать (2).
(2) В конкурентном коде следует придерживаться этой практики даже больше, чем обычно. Поскольку ошибки конкурентности чрезвычайно трудно воспроизводимы и не просты в отладке, преимущество небольшого прироста производительности на некоторых редко используемых ветвях кода может вполне оказаться ничтожным по сравнению с риском, что программа завершится аварийно в условиях эксплуатации.
Если вы решите, что вам необходимо нарушить инкапсуляцию, то не все потеряно. Вашу программу по‑прежнему можно сделать потокобезопасной, но процесс будет сложнее и дороже, а результат — ненадежнее. Глава 4 характеризует условия, при которых можно безопасно смягчать инкапсуляцию переменных состояния.
До сих пор мы использовали термины «потокобезопасный класс» и «потокобезопасная программа» почти взаимозаменяемо. Строится ли потокобезопасная программа полностью из потокобезопасных классов? Не обязательно: программа, которая состоит полностью из потокобезопасных классов, может не быть потокобезопасной, и потокобезопасная программа может содержать классы, которые не являются потокобезопасными. Вопросы, связанные с компоновкой потокобезопасных классов, также рассматриваются в главе 4. В любом случае понятие потокобезопасного класса имеет смысл только в том случае, если класс инкапсулирует собственное состояние. Термин «потокобезопасность» может применяться к коду, но он говорит о состоянии и может применяться только к тому массиву кода, который инкапсулирует его состояние (это может быть объект или вся программа целиком).
2.1. Что такое потокобезопасность?
Дать определение потокобезопасности непросто. Быстрый поиск в Google выдает многочисленные варианты, подобные этим:
… может вызываться из многочисленных потоков программы без нежелательных взаимодействий между потоками.
… может вызываться двумя или более потоками одновременно, не требуя никаких других действий с вызывающей стороны.
Учитывая подобные определения, неудивительно, что мы находим потокобезопасность запутанной! Как отличить потокобезопасный класс от небезопасного? Что мы вообще подразумеваем под словом «безопасный»?
В основе любого разумного определения потокобезопасности лежит понятие правильности (correctness).
Правильность подразумевает соответствие класса своей спецификации. Спецификация определяет инварианты (invariants), ограничивающие состояние объекта, и постусловия (postconditions), описывающие эффекты от операций. Как узнать, что спецификации для классов являются правильными? Никак, но это не мешает нам их использовать после того, как мы убедили себя, что код работает. Поэтому давайте допустим, что однопоточная правильность — это нечто видимое. Теперь можно предположить, что потокобезопасный класс ведет себя правильно во время доступа из многочисленных потоков.
Класс является потокобезопасным, если он ведет себя правильно во время доступа из многочисленных потоков, независимо от того, как выполнение этих потоков планируется или перемежается рабочей средой, и без дополнительной синхронизации или другой координации со стороны вызывающего кода.
Многопоточная программа не может быть потокобезопасной, если она не является правильной даже в однопоточной среде (3). Если объект реализован правильно, то никакая последовательность операций — обращения к публичным методам и чтение или запись в публичные поля — не должна нарушать его инварианты или постусловия. Ни один набор операций, выполняемых последовательно либо конкурентно на экземплярах потокобезопасного класса, не может побудить экземпляр находиться в недопустимом состоянии.
(3) Если нестрогое использование термина правильность здесь вас беспокоит, то вы можете думать о потокобезопасном классе как о классе, который неисправен в конкурентной среде, как и в однопоточной среде.
Потокобезопасные классы инкапсулируют любую необходимую синхронизацию сами и не нуждаются в помощи клиента.
2.1.1. Пример: сервлет без поддержки внутреннего состояния
В главе 1 мы перечислили структуры, которые создают потоки и вызывают из них компоненты, за потокобезопасность которых ответственны вы. Теперь мы намерены разработать сервлетную службу разложения на множители и постепенно расширить ее функционал, сохраняя потокобезопасность.
В листинге 2.1 показан простой сервлет, который распаковывает число из запроса, раскладывает его на множители и упаковывает результаты в отклик.
Листинг 2.1. Сервлет без поддержки внутреннего состояния
Класс StatelessFactorizer, как и большинство сервлетов, не имеет внутреннего состояния: не содержит полей и не ссылается на поля из других классов. Состояние для конкретного вычисления существует только в локальных переменных, которые хранятся в потоковом стеке и доступны только для выполняющего потока. Один поток, обращающийся к StatelessFactorizer, не может повлиять на результат другого потока, делающего то же самое, поскольку эти потоки не используют состояние совместно.
Объекты без поддержки внутреннего состояния всегда являются потокобезопасными.
Тот факт, что большинство сервлетов могут быть реализованы без поддержки внутреннего состояния, значительно снижает бремя по обеспечению потокобезопасности самих сервлетов. И только когда сервлеты должны что-то запомнить, требования к их потокобезопасности возрастают.
2.2. Атомарность
Что происходит при добавлении элемента состояния в объект без поддержки внутреннего состояния? Предположим, мы хотим добавить счетчик посещений, который измеряет число обработанных запросов. Можно добавить в сервлет поле с типом long и приращивать его при каждом запросе, как показано в UnsafeCountingFactorizer в листинге 2.2.
Листинг 2.2. Сервлет, подсчитывающий запросы без необходимой синхронизации. Так делать не следует
К сожалению, класс UnsafeCountingFactorizer не является потокобезопасным, даже если отлично работает в однопоточной среде. Так же, как UnsafeSequence, он предрасположен к потерянным обновлениям (lost updates). Хотя операция приращения ++count имеет компактный синтаксис, она не является атомарной (atomic), то есть неделимой, а представляет собой последовательность из трех операций: доставки текущего значения, прибавления к нему единицы и записи нового значения обратно. В операциях «прочитать, изменить, записать» результирующее состояние является производным от предыдущего.
На рис. 1.1 показано, что может произойти, если два потока попытаются увеличить счетчик одновременно, без синхронизации. Если счетчик равен 9, то из‑за неудачной временной координации оба потока увидят значение 9, добавят в него единицу, и установят значение 10. Так счетчик посещений начнет отставать на единицу.
Вы можете подумать, что наличие немного неточного счетчика посещений в веб‑службе является приемлемой потерей, и иногда это так. Но если счетчик используется для создания последовательностей или уникальных идентификаторов объектов, то возвращение одного и того же значения из многочисленных активаций может привести к серьезным проблемам целостности данных. Возможность появления неправильных результатов из-за неудачной временнˆой координации возникает при состоянии гонки.
2.2.1. Состояния гонки
Класс UnsafeCountingFactorizer имеет несколько состояний гонки (4). Наиболее распространенным типом состояния гонки является ситуация «проверить и затем действовать», где потенциально устаревшее наблюдение используется для принятия решения о том, что делать дальше.
(4) Термин состояние гонки часто путают с родственным термином гонка данных (data race). Гонка данных возникает, когда синхронизация не используется для координации всего доступа к общему нефинальному полю. Вы рискуете попасть в гонку данных всякий раз, когда поток пишет переменную, которая затем может быть прочитана другим потоком, либо считывает переменную, которая в последний раз могла быть записана другим потоком, если оба потока не используют синхронизацию. Код с гонками данных не имеет полезной формально определенной семантики в рамках модели памяти Java. Не все состояния гонки являются гонками данных, и не все гонки данных являются состояниями гонки, но оба типа ситуаций могут вызывать аварийный сбой конкурентных программ самым непредсказуемым образом. UnsafeCountingFactorizer содержит оба типа. Подробнее гонки данных описаны в главе 16.
Мы часто сталкиваемся с состоянием гонки в реальной жизни. Допустим, вы планируете встретиться с другом в полдень в кафе «Старбакс» на Университетском проспекте. Но вы узнаете, что на Университетском проспекте находятся два «Старбакса». В 12:10 вы не видите своего друга в кафе A и идете в кафе B, но там его тоже нет. Либо ваш друг опаздывает, либо он прибыл в кафе A сразу после того, как вы ушли, либо он был в кафе B, но пошел вас искать и теперь находится на пути к кафе A. Примем последний, то есть самый худший вариант. Сейчас 12:15, и вы оба задаетесь вопросом, а сдержал ли друг обещание. Вы вернетесь в другое кафе? Сколько раз вы будете ходить туда и обратно? Если вы не согласовали протокол, то можете провести весь день, гуляя по Университетскому проспекту в кофеиновой эйфории.
Проблема подхода «прогуляться и посмотреть, не находится ли он там» заключается в том, что прогулка по улице между двумя кафе занимает несколько минут, и за это время состояние системы может измениться.
Пример со «Старбаксом» иллюстрирует зависимость результата от относительной временной координации событий (от того, как долго вы ждете друга, находясь в кафе, и т. д.). Наблюдение, что он не находится в кафе A, становится потенциально утратившим силу: как только вы выходите из парадной двери, он может войти через заднюю дверь. Большинство состояний гонки вызывают такие проблемы, как неожиданное исключение, перезаписанные данные и повреждение файла.
2.2.2. Пример: состояния гонки в ленивой инициализации
Распространенным приемом, использующим подход «проверить и затем действовать», является ленивая инициализация (LazyInitRace). Ее цель — отложить инициализацию объекта до тех пор, пока он не понадобится, и обеспечить, чтобы он инициализировался только один раз. В листинге 2.3 метод getInstance убеждается в выполнении инициализации ExpensiveObject и возвращает существующий экземпляр, или, в противном случае, создает новый экземпляр и возвращает его после сохранения ссылки на него.
Листинг 2.3. Состояние гонки в ленивой инициализации. Так делать не следует
Класс LazyInitRace содержит состояния гонки. Предположим, что потоки A и B выполняют метод getInstance в одно и то же время. A видит, что поле instance равно null, и создает новый ExpensiveObject. Поток B также проверяет, равно ли поле instance тому же значению null. Наличие в поле значения null в этот момент зависит от временнˆой координации, включая капризы планирования и количество времени, нужного для создания экземпляра объекта ExpensiveObject и установки значения в поле instance. Если поле instance равно null, когда B его проверяет, два элемента кода, вызывающих метод getInstance, могут получить два разных результата, даже если метод getInstance предположительно должен всегда возвращать один и тот же экземпляр.
Счетчик посещений в UnsafeCountingFactorizer тоже содержит состояния гонки. Подход «прочитать, изменить, записать» подразумевает, что для приращения счетчика поток должен знать его предыдущее значение и убедиться, что в процессе обновления никто другой не изменяет и не использует это значение.
Как и большинство ошибок конкурентности, состояния гонки не всегда приводят к сбою: временная координация бывает удачной. Но если класс LazyInitRace используется для инстанциации реестра всего приложения, то, когда из многочисленных активаций он будет возвращать разные экземпляры, регистрации будут утеряны либо действия получат противоречивые представления набора зарегистрированных объектов. Или если класс UnsafeSequence используется для генерирования идентификаторов сущностей в структуре консервации данных, то два разных объекта могут иметь один и тот же идентификатор, нарушая ограничения идентичности.
2.2.3. Составные действия
И LazyInitRace, и UnsafeCountingFactorizer содержат последовательность операций, которые должны быть атомарными. Но для предотвращения состояния гонки должно существовать препятствие тому, чтобы другие потоки использовали переменную, пока один поток ее изменяет.
Операции A и B являются атомарными, если, с точки зрения потока, выполняющего операцию A, операция B либо была целиком выполнена другим потоком, либо не выполнена даже частично.
Атомарность операции приращения в UnsafeSequence позволила бы избежать состояния гонки, показанного на рис. 1.1. Операции «проверить и затем действовать» и «прочитать, изменить, записать» всегда должны быть атомарными. Они называются составными действиями (сompound actions) — последовательностями операций, которые должны выполняться атомарно, для того чтобы оставаться потокобезопасными. В следующем разделе мы рассмотрим блокировку — встроенный в Java механизм, который обеспечивает атомарность. А пока мы исправим проблему другим способом, применив существующий потокобезопасный класс, как показано в Countingfactorizer в листинге 2.4.
Листинг 2.4. Сервлет, подсчитывающий запросы с помощью AtomicLong
Пакет java.util.concurrent.atomic содержит атомарные переменные (atomic variable) для управления состояниями классов. Заменив тип счетчика с long на AtomicLong, мы гарантируем, что все действия, которые обращаются к состоянию счетчика, являются атомарными1. Поскольку состояние сервлета является состоянием счетчика, а счетчик является потокобезопасным, наш сервлет становится потокобезопасным.
При добавлении единственного элемента состояния в класс, который не поддерживает внутреннее состояние, результирующий класс будет потокобезопасным, если состояние полностью управляется потокобезопасным объектом. Но, как мы увидим в следующем разделе, переход от одной переменной состояния к следующим будет не так прост, как переход от нуля к единице.
Там, где это удобно, используйте существующие потокобезопасные объекты, такие как AtomicLong, для управления состоянием вашего класса. Возможные состояния существующих потокобезопасных объектов и их переходы в другие состояния легче поддерживать и проверять на потокобезопасность, нежели произвольные переменные состояния.
Для Хаброжителей скидка 25% по купону — Java
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.







