We plan to study the entire book in order, somtimes combining two
sections into a single session, sometimes splitting a section over two
sessions.
1.1 Using Concurrency Constructs
(1-18) |
Session 1 |
Introduces the basic
Java concurrency support constructs and the principal methods of class
Thread. |
1.2 Objects and Concurrency
(19-36) |
|
Describes the primary
uses of concurrency, the basic units used to implement concurrent execution,
and how these map onto object models. |
1.3 Design Forces (37-56) |
Session 2 |
Surveys the design concerns that arise in
concurrent software development, including safety, liveness, performance, and
reusability. |
1.4 Before/After Patterns
(57-68) |
Session 3 |
Discusses the primary
design patterns used to implement symmetrical control mechanisms. |
2.1 Immutability (69-74) |
Session 4 |
Discusses the uses of immutable and
partially immutable objects in concurrent programs. |
2.2 Synchronization (75-98) |
|
Discusses the serialized execution of
synchronized Java threads and the impact they have on objects. |
2.3 Confinement
(99-116) |
Session 5 |
Discusses encapsulation
techniques that structurally guarantee that only a single thread will ever
access some object(s) at a given time. |
2.4 Structuring and Refactoring Classes
(117-146) |
Sessions 6 + 7 |
Discusses strategies for removing
unnecessary synchronization, synchronization splitting, read-only operation
export, state isolation, and lock reuse. |
2.5 Using Lock Utilities
(147-158) |
Session 8 |
Discusses the
implementations of commonly used lock utility classes. |
3.1 Dealing with Failure (159-178) |
Session 9 |
Discusses exceptions and
cancellations. |
3.2 Guarded Methods
(179-198) |
Session 10 |
Introduces
the guard constructions used in conservative designs. |
3.3 Structuring and Refactoring Classes
(199-218) |
Sessions 11 + 12 |
Presents structural patterns for classes
employing concurrency control. |
3.4 Using Concurrency Control
Utilities (219-236) |
Session 13 |
Shows how utility
classes reduce complexity while improving reliability, performance, and
flexibility. |
3.5 Joint Actions (237-248) |
Session 14 |
Shows how to control actions that depend
on the states of multiple participants. |
3.6 Transactions
(249-264) |
Session 15 |
Provides a brief
overview of transactional concurrency control. |
3.7 Implementing Utilities (265-280) |
Session 16 |
Illustrates the techniques used in the
construction of some common utilities. |
4.1 Oneway Messages
(281-304) |
Session 17 |
Presents some basic
options for implementing oneway messages. |
4.2 Composing Oneway Messages (305-324) |
Session 18 |
Discusses the use of oneway messages in
component networks. |
4.3 Services in Threads
(325-342) |
Session 19 |
Presents alternatives
for implementing service threads. |
4.4 Parallel Decomposition (343-366) |
Session 20 |
Examines techniques used to improve
performance with multiple processors. |
4.5 Active Objects
(367-376) |
Session 21 |
Provides an overview of
constructs and frameworks for systems of active objects. |
Disclaimer: These questions can and should be augmented and / or
replaced by questions raised by the participating study group members.
Please bring your own questions to each study session!
1.1 Using Concurrency Constructs
(1-18) |
Session 1 |
- When would you want to create a daemon Thread?
- Why have suspend, resume, stop, destroy been
deprecated?
- When would it be useful to create a ThreadGroup?
|
|
1.2 Objects and Concurrency
(19-36) |
|
- What classes of problems can concurrent programs help solve
(hint: optimization)?
- What benefits do concurrent programs offer over sequential
programs?
- How can we decide whether a system needs separate machines,
processors, processes, or threads?
- What are the trade-offs (costs v. benefits) of each kind of
separation?
- What thread scheduling strategies does Java support?
|
|
1.3 Design Forces (37-56) |
Session 2 |
- How are performance and reusability related to safety and
liveness? What other qualities contribute to safety and liveness?
- What other kinds of safety concerns are there besides type
safety and multithread safety (hint: consider transactions and ACID
properties)?
- Doug Lea lists several qualities that can be used to measure
performance. How can we measure reuse and reusability?
|
|
1.4 Before/After Patterns
(57-68) |
Session 3 |
- What are the benefits and costs of the various approaches to
implementing before/after patterns?
- Is the cost of instantiating a method adapter worth the benefit
of factoring out the lifecycle guarantees into a single location (also see:
Resource Manager)?
|
|
2.1 Immutability (69-74) |
Session 4 |
- How can the fields of an object become accessible before its
construction is complete?
- Why is it important to ensure that flyweights are completely
constructed before they are published?
|
|
2.2 Synchronization (75-98) |
|
- When would it be useful to know which of several threads owns a
lock?
- When and why is fairness important during lock
acquisition?
- What are the benefits of reentrant locking? Are there any
drawbacks?
- When would it be appropriate to use a volatile field
instead of synchronized methods or blocks?
|
|
2.3 Confinement
(99-116) |
Session 5 |
- What are the trade-offs associated with tail-call
hand-offs?
- Given the consequences of thread-based confinement (page 106),
which of the available options seems most flexible?
- When would thread-based confinement be inappropriate?
- On page 112, the take protocol has often been called
orphan and the put protocol often called adopt (see
Taligent's Guide to Designing Programs). How can
these two protocols (however named) be combined to implement a clean and
confined resource transfer? (hint: use the stack, e.g.,
x.adoptResource(y.orphanResource()))
|
|
2.4 Structuring and Refactoring Classes
(117-146) |
Sessions 6 + 7 |
- What are semantic guarantees (hint: class invariants, method
post-conditions)? How can they be weakened?
- Given the fragility of double-checked locks and the likelihood
that
they will break or be used inappropriately, would you
ever use them? If so, when?
- Does a "single giant lock" offer a significant opportunity for
refactoring?
- Should the use of open calls always be documented to clarify
the contractual obligations of the called methods?
- Is splitting a class (via refactoring) preferable to merely
splitting a lock within the class? When would one be preferable to the
other?
- Are explicitly immutable interfaces preferable to runtime
immutability (hint: catching contract violations during compilation rather than
execution)?
- How can the
Resource Manager pattern be adapted to enforce the correct
usage of resources in open containers?
|
|
2.5 Using Lock Utilities
(147-158) |
Session 8 |
- How can the awkward before/after construction on page 148 be
simplified (hint: make acquire and release reentrant)?
- What kind of method adapter signature will support passed
parameters and returned results?
- Can shared resource allocations be pre-empted, or must Java
threads always coordinate and cooperate?
- Do coupled locks introduce a greater likelihood of races and
deadlocks between competing threads?
- If so, how can that be resolved (hint: coupled lock
managers)?
- What kind(s) of fairness policy(s) will ensure that read-write
locks prevent starvation between contending readers and writers?
|
|
3.1 Dealing with Failure (159-178) |
Session 9 |
- When is each of the six general responses to failed actions
appropriate (termination, continuation, rollback, recovery, retry,
handlers)?
- When are thread interruption checks automatically performed
within the Java library?
- When are thread interruption checks not performed, i.e., when
are threads dormant while waiting for a resource?
- Why are lock utilities useful for cancellation protocols (hint:
reduced dormancy)?
- What kinds of safety concerns arise in relation to
Thread.stop, i.e., why was it deprecated?
- What (relatively) safe alternatives are there for terminating a
thread and diminishing its use of system resources if the thread fails to
terminate?
|
|
3.2 Guarded Methods
(179-198) |
Session 10 |
- How do guarded methods extended synchronized methods?
How do guards differ from traditional conditionals?
- How does concurrent constraint programming help solve
state-based design problems?
- What are the commonly used alternatives for representing
state?
- How do predicate states differ from enumerated states? What are
their benefits?
- When can starvation become an issue in state-based concurrent
program designs?
- How can slipped conditions and missed signals be avoided?
- Why is notifyAll used more often than notify to
awaken waiting threads?
- Why is it better to avoid busy waits? What are better
alternatives?
|
|
3.3 Structuring and Refactoring Classes
(199-218) |
Sessions 11 + 12 |
- In general, how can logical state analysis help determine the
optimal usage of wait and notify during state transition
operations?
- The state table on page 200 defines the states and the legal
transitions for a BoundedBuffer. What additional information is needed to
determine the optimal usage of wait and notify during the state
transition operations?
- How can conflicts between a large set of operation pairs be
resolved with a minimum of custom code per each pair?
- How can the examples for tracked states and conflict sets be
improved with refactoring (hint: extract methods)?
- When can starvation become an issue with readers and
writers?
- Why is it useful to separate functionality as non-public
methods from concurrency control as public methods?
- How can lockouts be avoided in nested monitors?
|
|
3.4 Using Concurrency Control
Utilities (219-236) |
Session 13 |
- How are semaphores related to mutual exclusion locks, resource
pools, bounded buffers, and synchronous channels?
- In what kind of applications will fairness issues usually
arise?
- What is priority inversion, and how can it be countered?
- What are the typical applications of binary latches?
- What rarely useful mechanism finds an appropriate usage in
latching variables?
- When are exchangers useful (hint: double buffering)?
- When are condition variables useful (hint: legacy code
conversion)?
|
|
3.5 Joint Actions (237-248) |
Session 14 |
- What are some of the factors to consider when designing joint
actions?
- What is the main goal of joint action designs?
- What are the general structure and behavior of classes involved
in joint actions?
- What are some of the conflict resolution strategies used to
prevent deadlocks in joint action designs?
- What is the best way to avoid design issues associated with
joint actions?
|
|
3.6 Transactions
(249-264) |
Session 15 |
- What are the four steps in the basic transaction protocol?
- What are the two complementary sets of policies that can be
applied to transaction protocols?
- How do these two policies effect the design of transactional
interfaces and implementations?
- When would it make sense to support both optimistic and
conservative transaction policies?
- How can the detailed analysis of the structure of transaction
policies help determine which to choose (hint: cost comparison)?
- What is the relationship between a property constraint and the
ability (right) to veto a property change?
|
|
3.7 Implementing Utilities (265-280) |
Session 16 |
- Given the complexity of the methods in Semaphore, how would you
refactor them with Extract Method?
- What are the potential performance costs of using
notifyAll versus notify?
- Given that single-threaded notification designs usually
increase design complexity, will it usually be worth the performance gain to
pursue such designs?
- Why might collapsing the classes within a design that splits
state-dependent actions make them more efficient?
- Given the increased complexity from collapsing classes, when
(if ever) would this approach be warranted?
- Is there an appropriate utility class that could be used in
place of WaitQueue for the FIFO semaphore?
- In comparison with the suggested FIFO semaphore implementation,
how could a priority semaphore be implemented?
- How could a task-oriented semaphore for resolving conflict sets
be designed?
- How could the queuing policy for the various kinds of
queue-based semaphores be factored out?
|
|
4.1 Oneway Messages
(281-304) |
Session 17 |
- What data characteristics distinguish the various kinds of
message formats described in section 4.1.1 (hint: binary v. text, instance v.
class, event v. 1-way request)?
- In an open call design, what will happen if the request arrival
rate exceeds the request acceptance rate (which is determined by the local
state update latency)?
- What are some of the reasons one might use a thread-per-message
design? What kind of limitations will usually be encountered with
thread-per-message designs?
- What trade-offs do thread pools introduce? How can thread pool
saturation be addressed? How do web servers and app servers typically address
saturation?
- Given that a Swing event queue is single-threaded, can it be
easily saturated? What are the observable consequences of such saturation? How
can such saturation be eliminated?
- How does the JDK Timer framework in java.util compare with that
suggested in section 4.1.4.3? How might a Schedule be represented indepently of
tasks, threads, Timers, and the system clock?
- When using a busy-wait loop to manage event-driven tasks, would
it be beneficial to use the number of tasks (or the average per some time
period) to control the sleep / wait duration?
- When commands do not arrive as units, a worker thead can stall.
Is there an alternative to using a buffering scheme to prevent stalling (hint:
test for available() >= N, where N is a fixed command length)?
|
|
4.2 Composing Oneway Messages (305-324) |
Session 18 |
- What are some of the goals satisfied by flow network designs
(hint: see the end of section 4.2.1.2)?
- What are some examples of systems from your development
practice that either did benefit or would benefit from the use of a flow
network design?
- What were the idioms, patterns, and metaphors relevant to your
designs?
- How do they compare with those described in this section?
|
|
4.3 Services in Threads
(325-342) |
Session 19 |
- How have you (or would you) use Thread.join and futures
in your own concurrent program designs?
- If the Callable interface in section 4.3.3.1 is awkward,
how would you improve upon it or generate a design based on this idea?
- How does the "elevator algorithm" from section 4.3.4 work? Is
there any situation from your own coding practice where you (could) have
applied this?
|
|
4.4 Parallel Decomposition (343-366) |
Session 20 |
- What are the primary goals we're trying to satisfy with task
granularity and structure in fork/join decomposition solutions?
- How do the forces represented by these goals trade off against
each other?
- What design techniques can be applied to balancing these
forces, i.e., what frameworks and design steps can be used?
- How can the number of forked subtasks be varied
dynamically?
- When are callback-based fork/join designs typically used?
- When and how can trees improve the efficiency of fork/join
designs?
- Is there any situation from your own coding practice where you
(could) have used a cyclic barrier?
|
|
4.5 Active Objects
(367-376) |
Session 21 |
- What differentiates active objects from other kinds of
objects?
- Is there any situation from your own coding practice where you
(could) have used active objects?
- How do CSP processes and channels behave?
- What benefits can be gained from using CSP in designs?
- Is there any situation from your own coding practice where you
(could) have used CSP?
|
|
Java is a trademark of Sun Microsystems, Inc.