Система Orphus

Java Concurrency in Practice

Авторы: Brian Goetz
Tim Peierls
Joshua Bloch
Joseph Bowbeer
David Holmes
Doug Lea
Издательство: Addison Wesley Professional, 2006
384 страницы

Материал предоставил: Михаил Воронцов
Найти в магазинах

От издателя

Содержание
Комментарии
Пример кода из книги

От издателя

At this writing, multicore processors are just now becoming inexpensive enough for midrange desktop systems. Not coincidentally, many development teams are noticing more and more threading-related bug reports in their projects. In a recent post on the NetBeans developer site, one of the core maintainers observed that a single class had been patched 14 times to fix threading-related problems. Dion Almaer, former editor of TheServerSide, recently blogged (after a painful debugging session that ultimately revealed a threading bug) that most Java programs are so rife with concurrency bugs that they work only "by accident".

Indeed, developing, testing and debugging multithreaded programs can be extremely difficult because concurrency bugs do not manifest themselves predictably. And when they do surface, it is often at worst possible time - in production, under heavy load.

One of the challenges of developing concurrent programs in Java is the mismatch between concurrency features offered by the platform and how developers need to think about concurrency in their programs. The language provides low-level mechanisms such as synchronization and condition waits, but these mechanisms must be used consistently to implement application-level protocols or policies. Without such policies, it is all too easy to create programs that compile and appear to work but are nevertheless broken. Many otherwise excellent books on concurrency fall short on their goal by focusing excessively on low-level mechanisms and APIs rather than design-level policies and patterns.

Java 5.0 is a huge step forward for the development of concurrent applications in Java, providing new higher-level components and additional low-level mechanisms that make it easier for novices and experts alike to build concurrent applications. The authors are primary members of the JCP Expert Group that created these facilities; in addition to describing their behavior and features, we present the underlying design patterns and anticipated usage scenarios that motivated their inclusion in the platform libraries.

Our goal is to give readers a set of design rules and mental models that make it easier - and more fun - to build correct, performant concurrent classes and applications in Java.

Содержание

Copyright
Advance Praise for Java Concurrency in Practice
Listings

Preface

Chapter 1. Introduction

Section 1.1. A (Very) Brief History of Concurrency
Section 1.2. Benefits of Threads
Section 1.3. Risks of Threads
Section 1.4. Threads are Everywhere

Part I: Fundamentals

Chapter 2. Thread Safety

Section 2.1. What is Thread Safety?
Section 2.2. Atomicity
Section 2.3. Locking
Section 2.4. Guarding State with Locks
Section 2.5. Liveness and Performance

Chapter 3. Sharing Objects

Section 3.1. Visibility
Section 3.2. Publication and Escape
Section 3.3. Thread Confinement
Section 3.4. Immutability
Section 3.5. Safe Publication

Chapter 4. Composing Objects

Section 4.1. Designing a Thread-safe Class
Section 4.2. Instance Confinement
Section 4.3. Delegating Thread Safety
Section 4.4. Adding Functionality to Existing Thread-safe Classes
Section 4.5. Documenting Synchronization Policies

Chapter 5. Building Blocks

Section 5.1. Synchronized Collections
Section 5.2. Concurrent Collections
Section 5.3. Blocking Queues and the Producer-consumer Pattern
Section 5.4. Blocking and Interruptible Methods
Section 5.5. Synchronizers
Section 5.6. Building an Efficient, Scalable Result Cache

Summary of Part I

Part II: Structuring Concurrent Applications

Chapter 6. Task Execution

Section 6.1. Executing Tasks in Threads
Section 6.2. The Executor Framework
Section 6.3. Finding Exploitable Parallelism
Summary

Chapter 7. Cancellation and Shutdown

Section 7.1. Task Cancellation
Section 7.2. Stopping a Thread-based Service
Section 7.3. Handling Abnormal Thread Termination
Section 7.4. JVM Shutdown
Summary

Chapter 8. Applying Thread Pools

Section 8.1. Implicit Couplings Between Tasks and Execution Policies
Section 8.2. Sizing Thread Pools
Section 8.3. Configuring ThreadPoolExecutor
Section 8.4. Extending ThreadPoolExecutor
Section 8.5. Parallelizing Recursive Algorithms
Summary

Chapter 9. GUI Applications

Section 9.1. Why are GUIs Single-threaded?
Section 9.2. Short-running GUI Tasks
Section 9.3. Long-running GUI Tasks
Section 9.4. Shared Data Models
Section 9.5. Other Forms of Single-threaded Subsystems
Summary

Part III: Liveness, Performance, and Testing

Chapter 10. Avoiding Liveness Hazards

Section 10.1. Deadlock
Section 10.2. Avoiding and Diagnosing Deadlocks
Section 10.3. Other Liveness Hazards
Summary

Chapter 11. Performance and Scalability

Section 11.1. Thinking about Performance
Section 11.2. Amdahl's Law
Section 11.3. Costs Introduced by Threads
Section 11.4. Reducing Lock Contention
Section 11.5. Example: Comparing Map Performance
Section 11.6. Reducing Context Switch Overhead
Summary

Chapter 12. Testing Concurrent Programs

Section 12.1. Testing for Correctness
Section 12.2. Testing for Performance
Section 12.3. Avoiding Performance Testing Pitfalls
Section 12.4. Complementary Testing Approaches
Summary

Part IV: Advanced Topics

Chapter 13. Explicit Locks

Section 13.1. Lock and ReentrantLock
Section 13.2. Performance Considerations
Section 13.3. Fairness
Section 13.4. Choosing Between Synchronized and ReentrantLock
Section 13.5. Read-write Locks
Summary

Chapter 14. Building Custom Synchronizers

Section 14.1. Managing State Dependence
Section 14.2. Using Condition Queues
Section 14.3. Explicit Condition Objects
Section 14.4. Anatomy of a Synchronizer
Section 14.5. AbstractQueuedSynchronizer
Section 14.6. AQS in Java.util.concurrent Synchronizer Classes
Summary

Chapter 15. Atomic Variables and Nonblocking Synchronization

Section 15.1. Disadvantages of Locking
Section 15.2. Hardware Support for Concurrency
Section 15.3. Atomic Variable Classes
Section 15.4. Nonblocking Algorithms
Summary

Chapter 16. The Java Memory Model

Section 16.1. What is a Memory Model, and Why would I Want One?
Section 16.2. Publication
Section 16.3. Initialization Safety
Summary

Appendix A. Annotations for Concurrency

Section A.1. Class Annotations
Section A.2. Field and Method Annotations

Bibliography
Index

Комментарии

Михаил Воронцов

Книга будет полезна в первую очередь для Java разработчиков. Все повествование в книге идет вокруг джавовских примитивов синхронизации и классов, полезных при написании параллельных программ. Особого растеканию мыслию по древу в сторону общей теории написания параллельных программ я не заметил. Как и написано в предисловии, книгу отличает практический подход к изложению. В ней вкратце упоминается класс Java (обычно в виде списка прототипов его методов), а затем идет развернутое описание того, в каких случаях его полезно использовать. В книге очень много небольших листингов (в среднем около 20 на главу). В них отражаются все моменты, описываемые в тексте книги. Например, в 6-ой главе почти все листинги "крутятся" вокруг различных реализаций и способов использования интерфейса Executor. Очень сильной стороной книги я считаю то, что в ней прекрасно описаны высокоуровневые конструкции для написания параллельных программ, тогда как большинство увиденных мною глав в различных книгах крутилось именно вокруг низкоуровневой синхронизации (которая во многих случаях просто неуместна при наличии высокоуровневых примитивов). У книги есть официальный сайт: http://javaconcurrencyinpractice.com/ , где тоже есть кое-что интересное.

Пример кода из книги

Хочу привести один пример законченного класса из этой книги. Класс реализует мемоизацию - процесс запоминания ранее вычисленных значений функции от ее аргументов. Например, если ранее уже было посчитано f(27), то при следующем таком же вызове значение функции считаться не будет. Вместо этого будет возвращено ранее сохраненное значение.

public class Memoizer<A, V> implements Computable<A, V> {
   private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
   private final Computable<A, V> c;

   public Memoizer(Computable<A, V> c) {this.c = c;}

   public V compute(final A arg) throws InterruptedException {
      while (true) {
         Future<V> f = cache.get(arg);
         if (f == null) {
            Callable<V> eval = new Callable<V>() {
               public V call() throws IntrruptedException {
                  return c.compute(arg);
               }
            };
            FutureTask<V> ft = new FutureTask<V>(eval);
            f = cache.putIfAbsent(arg, ft);
            if (f == null) { f = ft; ft.run(); }
         }
         try {
            return f.get();
         } catch (CancellationException e) {
            cache.remove(arg, f);
         } catch (ExecutionException e) {
            throw launderThrowable(e.getCause());
         }
      }
   }

   public static RuntimeException launderThrowable(Throwable t)
   {
      if (t instanceof RuntimeException)
         return (RuntimeException)t;
      else if (t instanceof Error)
         throw (Error)t;
      else
         throw new IllegalStateException("Not unchecked", t);
   }
}

И пример использования данного класса:

public class Factorizer implements Servlet {
   private final Computable<BigInteger, BigInteger[]> cc = 
      new Computable<BigInteger, BigInteger[]>() {
         public BigInteger[] compute(BigInteger arg) {
            return factor(arg);
         }
      };
   private final Computable<BigInteger, BigInteger[]> cache
      = new Memoizer<BigInteger, BigInteger[]>(cc);

   public void service(ServletRequest req,
            ServletResponse resp) {
      try {
         BigInteger i = extractFromRequest(req);
         encodeIntoResponse(resp, cache.compute(i));
      } catch (InterruptedException e) {
         encodeError(resp, "factorization interrupted");
      }
   }
}