Lecture 16: Thread Safety in Java

Similar documents
Concurrency WS 2010/2011 The Java Memory Model

Lecture 17: Sharing Objects in Java

Recap. Contents. Reenterancy of synchronized. Explicit Locks: ReentrantLock. Reenterancy of synchronise (ctd) Advanced Thread programming.

Sharing Objects. Java and Android Concurrency.

Synchronization SPL/2010 SPL/20 1

Thread Safety. Review. Today o Confinement o Threadsafe datatypes Required reading. Concurrency Wrapper Collections

Learning from Bad Examples. CSCI 5828: Foundations of Software Engineering Lecture 25 11/18/2014

Lecture 24: Java Threads,Java synchronized statement

SSC - Concurrency and Multi-threading Java multithreading programming - Synchronisation (II)

COMP 322: Fundamentals of Parallel Programming

Lecture 8: September 30

Threads and Java Memory Model

Lecture 19: Composing Objects in Java

Lecture 29: Java s synchronized statement

Computation Abstractions. Processes vs. Threads. So, What Is a Thread? CMSC 433 Programming Language Technologies and Paradigms Spring 2007

Introduction to Locks. Intrinsic Locks

Concurrency in Object Oriented Programs 1. Object-Oriented Software Development COMP4001 CSE UNSW Sydney Lecturer: John Potter

Advanced MEIC. (Lesson #18)

+ Today. Lecture 26: Concurrency 3/31/14. n Reading. n Objectives. n Announcements. n P&C Section 7. n Race conditions.

A Sophomoric Introduction to Shared-Memory Parallelism and Concurrency Lecture 5 Programming with Locks and Critical Sections

Get out, you will, of this bind If, your objects, you have confined

Lecture 9: Introduction to Monitors

More thread safety and Concurrent collections. Nasser Giacaman CompSci 230: Software Design and Construction

Safety SPL/2010 SPL/20 1

CMSC 330: Organization of Programming Languages

Virtual Machine Design

CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections. Tyler Robison Summer 2010

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

Thread-Local. Lecture 27: Concurrency 3. Dealing with the Rest. Immutable. Whenever possible, don t share resources

Advanced concurrent programming in Java Shared objects

The Java Memory Model

The New Java Technology Memory Model

Multithreaded Programming Part II. CSE 219 Stony Brook University, Department of Computer Science

Shared Mutable State SWEN-220

Atomicity CS 2110 Fall 2017

Sharing Objects Ch. 3

G Programming Languages Spring 2010 Lecture 13. Robert Grimm, New York University

Synchronization. CS61, Lecture 18. Prof. Stephen Chong November 3, 2011

Need for synchronization: If threads comprise parts of our software systems, then they must communicate.

Part I: Communication and Networking

Synchronization COMPSCI 386

CSE 374 Programming Concepts & Tools

Safety and liveness for critical sections

L13: Synchronization

Lecture 7: Process & Thread Introduction

Overview. CMSC 330: Organization of Programming Languages. Concurrency. Multiprocessors. Processes vs. Threads. Computation Abstractions

Real-Time and Concurrent Programming Lecture 4 (F4): Monitors: synchronized, wait and notify

CS533 Concepts of Operating Systems. Jonathan Walpole

Resource management. Real-Time Systems. Resource management. Resource management

Warm-up question (CS 261 review) What is the primary difference between processes and threads from a developer s perspective?

CPSC/ECE 3220 Fall 2017 Exam Give the definition (note: not the roles) for an operating system as stated in the textbook. (2 pts.

Java Concurrency in practice Chapter 9 GUI Applications

Thread Synchronization: Too Much Milk

Real-Time Systems. Lecture #4. Professor Jan Jonsson. Department of Computer Science and Engineering Chalmers University of Technology

Java Monitors. Parallel and Distributed Computing. Department of Computer Science and Engineering (DEI) Instituto Superior Técnico.

CS 455: INTRODUCTION TO DISTRIBUTED SYSTEMS [THREADS] Frequently asked questions from the previous class survey

Programming in Parallel COMP755

Concurrency: a crash course

Concurrent & Distributed Systems Supervision Exercises

THE UNIVERSITY OF WESTERN AUSTRALIA SAMPLE EXAM QUESTIONS 2007 WITH SOLUTIONS SCHOOL OF COMPUTER SCIENCE CITS3213 CONCURRENT PROGRAMMING (PART II)

Concurrency & Parallelism. Threads, Concurrency, and Parallelism. Multicore Processors 11/7/17

Concurrent Processes Rab Nawaz Jadoon

Models of concurrency & synchronization algorithms

CS 153 Design of Operating Systems Winter 2016

Programmazione di sistemi multicore

Potential violations of Serializability: Example 1

Lecture 21: Transactional Memory. Topics: consistency model recap, introduction to transactional memory

G52CON: Concepts of Concurrency

Dealing with Issues for Interprocess Communication

Threads Questions Important Questions

Programmazione di sistemi multicore

Java Threads Vs Processes. CS Concurrent Programming. Java Threads. What can a thread do? Java Concurrency

Synchronization: Advanced

Threads and Parallelism in Java

Reminder from last time

Java Memory Model for practitioners. by Vadym Kazulkin and Rodion Alukhanov, ip.labs GmbH

CMSC 132: Object-Oriented Programming II

CS 2112 Lecture 20 Synchronization 5 April 2012 Lecturer: Andrew Myers

G52CON: Concepts of Concurrency

CS 5523 Operating Systems: Midterm II - reivew Instructor: Dr. Tongping Liu Department Computer Science The University of Texas at San Antonio

CSE 332: Data Structures & Parallelism Lecture 17: Shared-Memory Concurrency & Mutual Exclusion. Ruth Anderson Winter 2019

Atomic Variables & Nonblocking Synchronization

DISTRIBUTED COMPUTER SYSTEMS

CSL373: Lecture 5 Deadlocks (no process runnable) + Scheduling (> 1 process runnable)

CST242 Concurrency Page 1

Concurrent Preliminaries

Threads, Concurrency, and Parallelism

CSE 332: Locks and Deadlocks. Richard Anderson, Steve Seitz Winter 2014

Effective Concurrent Java. Brian Goetz Sr. Staff Engineer, Sun Microsystems

Foundations of the C++ Concurrency Memory Model

CS 333 Introduction to Operating Systems. Class 3 Threads & Concurrency. Jonathan Walpole Computer Science Portland State University

Monitors; Software Transactional Memory

Concurrency Control. Synchronization. Brief Preview of Scheduling. Motivating Example. Motivating Example (Cont d) Interleaved Schedules

Synchronization in Java

Deadlock and Monitors. CS439: Principles of Computer Systems September 24, 2018

G52CON: Concepts of Concurrency

250P: Computer Systems Architecture. Lecture 14: Synchronization. Anton Burtsev March, 2019

Composing Objects. Java and Android Concurrency.

G52CON: Concepts of Concurrency

COMP 346 WINTER Tutorial 2 SHARED DATA MANIPULATION AND SYNCHRONIZATION

Transcription:

COMP 150-CCP Concurrent Programming Lecture 16: Thread Safety in Java Dr. Richard S. Hall rickhall@cs.tufts.edu Concurrent programming March 13, 2008

Reference The content of this lecture is based on Chapter 2 of the Java Concurrency in Practice book by Brian Goetz.

Java and Concurrency Threads are everywhere in Java JVM housekeeping (e.g., garbage collection, finalization) Main thread for running application AWT & Swing threads for events Timer class for deferred tasks Component frameworks such as Servlets and RMI create pools of threads In Java your application is likely to be multithreaded whether you know it or not Thus, you have to be familiar with concurrency and thread safety

State Management Concurrent programming is not really about threads or locks, these are simply mechanisms At its core, it is about managing access to state, particularly shared, mutable state In Java, this state is the data fields of objects An object's state encompasses any data that can affect its externally visible behavior

Need for Thread Safety Depends on whether object will be accessed from multiple threads This is a property of how the object will be used, not what it does If multiple threads can access an object and one of them might write to it, then they all must coordinate access using synchronization There are no special situations where this rule does not apply

Achieving Thread Safety If multiple threads access the same mutable state variable without appropriate synchronization, then your program is broken

Achieving Thread Safety If multiple threads access the same mutable state variable without appropriate synchronization, then your program is broken There are three ways to fix it Don't share the state variable across threads Make the state variable immutable Use synchronization whenever accessing the state variable

Achieving Thread Safety If multiple threads access the same mutable state variable without appropriate synchronization, then your program is broken There are three ways to fix it Don't share the state variable across threads Make the state variable immutable Use synchronization whenever accessing the state variable None of these are necessarily as easy as they may sound

Thread-Safe Classes A class is thread safe when it continues to behave correctly when accessed from multiple threads

Thread-Safe Classes A class is thread safe when it continues to behave correctly when accessed from multiple threads Regardless of scheduling or interleaving of execution

Thread-Safe Classes A class is thread safe when it continues to behave correctly when accessed from multiple threads Regardless of scheduling or interleaving of execution No set of operations performed sequentially or concurrently on instances of thread-safe classes can cause an instance to be in an invalid state

Thread-Safe Classes A class is thread safe when it continues to behave correctly when accessed from multiple threads Regardless of scheduling or interleaving of execution No set of operations performed sequentially or concurrently on instances of thread-safe classes can cause an instance to be in an invalid state Any needed synchronization is encapsulated in the class so that clients need not provide their own The concept of a thread-safe class only makes sense if the class fully encapsulates its state Likewise for the entire body of code that comprises a thread-safe program

Thread-Safe Class vs Program Is a thread-safe program simply a program constructed of thread-safe classes?

Thread-Safe Class vs Program Is a thread-safe program simply a program constructed of thread-safe classes? No All thread-safe classes can still result in non-thread-safe programs A thread-safe program may use non-thread-safe classes

Thread-Safety Example Stateless factorizing servlet public class StatelessFactorizer implements Servlet { public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); encodeintoresponse(resp, factors);

Thread-Safety Example Stateless factorizing servlet public class StatelessFactorizer implements Servlet { public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); encodeintoresponse(resp, factors); Has no fields and references no fields from other classes; everything is on the stack. Therefore, it is thread safe.

Thread-Safety Example Stateless factorizing servlet public class StatelessFactorizer implements Servlet { public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); encodeintoresponse(resp, factors); Stateless objects are always thread safe.

Thread-Safety Example Stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private long count = 0; public long getcount() { return count; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); ++count; encodeintoresponse(resp, factors);

Thread-Safety Example Stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private long count = 0; public long getcount() { return count; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); ++count; encodeintoresponse(resp, factors); This would work fine with in a single-threaded program, but not in a multithreaded one...this is susceptible to lost updates.

Thread-Safety Example Stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private long count = 0; public long getcount() { return count; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); ++count; encodeintoresponse(resp, factors); This is a read-modifywrite race condition.

Race Conditions A race condition is the possibility of incorrect results due to timing of execution

Race Conditions A race condition is the possibility of incorrect results due to timing of execution Most common form is check-then-act A stale observation is used to determine what to do next We've seen this in our homework where we have used individually atomic actions to test and the perform some action

Race Conditions A race condition is the possibility of incorrect results due to timing of execution Most common form is check-then-act A stale observation is used to determine what to do next We've seen this in our homework where we have used individually atomic actions to test and the perform some action Similar to what happens in real-life if you try to meet someone... Need to have some agreed upon protocol

Race Condition Example Lazy initialization public class LazyInit { private ExpensiveObject instance = null; public ExpensiveObject getinstance() { if (instance == null) instance = new ExpensiveObject(); return instance;

Race Condition Example Lazy initialization public class LazyInit { private ExpensiveObject instance = null; public ExpensiveObject getinstance() { if (instance == null) instance = new ExpensiveObject(); return instance; Unfortunate timing could result in this method returning different instances.

Compound Actions Read-modify-write and check-then-act operation sequences are compound actions To ensure thread safety, all constituent actions must be performed atomically An operation or sequence of operations is atomic if it is indivisible relative to other operations on the same state i.e., other threads see it as either happening completely or not at all.

Thread-Safety Example Modified stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private final AtomicLong count = new AtomicLong(0); public long getcount() { return count.get(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); count.incrementandget(); encodeintoresponse(resp, factors);

Thread-Safety Example Modified stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private final AtomicLong count = new AtomicLong(0); public long getcount() { return count.get(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); count.incrementandget(); encodeintoresponse(resp, factors); Since this uses a thread-safe AtomicLong type from java.util.concurrent.atomic, the class is once again thread safe.

Thread-Safety Example Modified stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private final AtomicLong count = new AtomicLong(0); AtomicLong provides an atomic read-modifywrite operation for incrementing the value. public long getcount() { return count.get(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); count.incrementandget(); encodeintoresponse(resp, factors);

Thread-Safety Example Modified stateful factorizing servlet Keeps track of how many times it has been invoked public class CountingFactorizer implements Servlet { private final AtomicLong count = new AtomicLong(0); Advice: Where practical, use existing thread-safe objects to manage your state. public long getcount() { return count.get(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = factor(i); count.incrementandget(); encodeintoresponse(resp, factors);

Side Note: AtomicLong AtomicLong replaces a long/long get() - Gets the current value. set(long newvalue) - Sets to the given value. lazyset(long newvalue) - Eventually sets to the given value. compareandset(long expect, long update) - Atomically sets the value to the given updated value if the current value == the expected value. weakcompareandset(long expect, long update) - Atomically sets the value to the given updated value if the current value == the expected value. getandadd(long delta) - Atomically adds the given value to the current value. getanddecrement() - Atomically decrements by one the current value. getandincrement() - Atomically increments by one the current value. getandset(long newvalue) - Atomically sets to the given value and returns the old value. addandget(long delta) - Atomically adds the given value to the current value. incrementandget() - Atomically increments by one the current value. decrementandget() - Atomically decrements by one the current value.

Thread-Safety Example Caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors);

Thread-Safety Example Caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors); Even though our two references are atomic, they are not independent.

Thread-Safety Example Caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors); Thus, dependent operations on them must be done atomically...

Thread-Safety Example Caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors); And here too.

Thread-Safety Example Caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors); Thus, this class is not thread safe.

Side Note: AtomicReference<V> AtomicReference<V> can be used in place of a reference to an object get() - Gets the current value. set(v newvalue) - Sets to the given value. lazyset(v newvalue) - Eventually sets to the given value. getandset(v newvalue) - Atomically sets to the given value and returns the old value. compareandset(v expect, V update) - Atomically sets the value to the given updated value if the current value == the expected value. weakcompareandset(v expect, V update) - Atomically sets the value to the given updated value if the current value == the expected value.

Intrinsic Locks Java offers built-in locking to enforce atomicity via the synchronized block One lock associated with each object instance A synchronized block is comprised of An object reference that is used as the lock A block of code that is guarded by the lock Only one thread at any given time can be inside of a block guarded by a given lock (i.e, mutual exclusion) The lock is acquired/released by the thread on entry/exit It may be blocked to wait to acquire the lock Although each object has a lock, that lock can be used for any purpose Not necessarily related to the object itself Possibly spanning many objects

Intrinsic Locks A lock associated with an object does not restrict access to the object's state It only restricts multiple threads from acquiring the lock at the same time Having a built-in object lock is only a convenience so that we don't have to explicitly create locks Encapsulation with an appropriate locking protocol is the only way to restrict access to an object's state

Intrinsic Locks public class Foo { public synchronized void bar() {... Is equivalent to: public class Foo { public void bar() { synchronized (this) {...

Intrinsic Locks public class Foo { public synchronized void bar() {... Is equivalent to: public class Foo { public void bar() { synchronized (this) {... Why might you do this instead?

Intrinsic Locks public class Foo { public synchronized void bar() {... Is equivalent to: public class Foo { public void bar() { synchronized (this) {... To provide more finegrained locking.

Intrinsic Locks Assuming Foo is thread safe, what can we assume about bar() and woz()? public class Foo { public synchronized void bar() {... public void woz() {...

Intrinsic Locks Assuming Foo is thread safe, what can we assume about bar() and woz()? public class Foo { public synchronized void bar() {... public void woz() {... Perhaps that the woz() method does not access shared state...

Intrinsic Locks Assuming Foo is thread safe, what can we assume about bar() and woz()? public class Foo { public synchronized void bar() {... public void woz() {... Or that woz() has more fine-grained locking...

Intrinsic Locks Assuming Foo is thread safe, what can we assume about bar() and woz()? public class Foo { public synchronized void bar() {... public void woz() {... At a minimum, we know that more than one thread can enter woz() and thus the Foo instance at a given time.

Intrinsic Locks How does sharing locks across objects work? public class Foo { public Foo(String s) {... public void foo() { synchronized (s) {... public class Bar { public Bar(String s) {... public void bar() { synchronized (s) {...... String s = new String( l ); Foo f = new Foo(s); Bar b = new Bar(s);...

Intrinsic Locks How does sharing locks across objects work? public class Foo { public Foo(String s) {... public void foo() { synchronized (s) {... public class Bar { public Bar(String s) {... public void bar() { synchronized (s) {...... String s = new String( l ); Foo f = new Foo(s); Bar b = new Bar(s);... As you would expect, only one thread can be executing inside the guarded code blocks of Foo or Bar at a given time.

Thread-Safety Example Modified caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public synchronized void service( ServletRequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors);

Thread-Safety Example Modified caching factorizing servlet Remembers last result public class CachingFactorizer implements Servlet { private final AtomicReference<BigInteger> lastnumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastfactors = new AtomicReference<BigInteger[]>(); public synchronized void service( ServletRequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); if (i.equals(lastnumber.get())) encodeintoresponse(resp, lastfactors.get()); else { BigInteger[] factors = factor(i); lastnumber.set(i); lastfactors.set(factors); encodeintoresponse(resp, factors); This does achieve thread safety, but it no longer allows concurrent execution; thus, its performance is worse.

Intrinsic Locks Would the following code deadlock? public class Widget { public synchronized void dosomething() {... public class LoggingWidget extends Widget { public synchronized void dosomething() { System.out.println("Calling dosomething"); super.dosomething();

Intrinsic Locks Would the following code deadlock? public class Widget { public synchronized void dosomething() {... No, because intrinsic locks are reentrant. public class LoggingWidget extends Widget { public synchronized void dosomething() { System.out.println("Calling dosomething"); super.dosomething();

Intrinsic Locks Intrinsic locks are acquired per thread, not per invocation Semaphores are acquired per invocation, for example

Intrinsic Locks Intrinsic locks are acquired per thread, not per invocation Semaphores are acquired per invocation, for example Essentially, Java remembers the thread that owns a lock and keeps a lock counter The counter value for unheld locks is zero Each time a given thread acquires a lock, it increments the counter Likewise, it decrements the counter each time it exits a synchronized block until it reaches zero and the lock is freed

Intrinsic Locks Intrinsic locks are acquired per thread, not per invocation Semaphores are acquired per invocation, for example Essentially, Java remembers the thread that owns a lock and keeps a lock counter The counter value for unheld locks is zero Each time a given thread acquires a lock, it increments the counter Likewise, it decrements the counter each time it exits a synchronized block until it reaches zero and the lock is freed Reentrancy facilitates encapsulation of locking behavior and simplifies development objectoriented concurrent code

Locking Rules of Thumb Compound actions on shared state must be made atomic (i.e., read-modify-write, check-then-act)

Locking Rules of Thumb Compound actions on shared state must be made atomic (i.e., read-modify-write, check-then-act) Combining individually atomic actions does not result in an atomic action

Locking Rules of Thumb Compound actions on shared state must be made atomic (i.e., read-modify-write, check-then-act) Combining individually atomic actions does not result in an atomic action All access to shared state must be synchronized, not just modifications

Locking Rules of Thumb Compound actions on shared state must be made atomic (i.e., read-modify-write, check-then-act) Combining individually atomic actions does not result in an atomic action All access to shared state must be synchronized, not just modifications Use same lock wherever a specific variable is accessed Variable is considered to be guarded by the specific lock Also true if multiple variables make up a single invariant You should clearly document which locks are used to guard which state

Locking Rules of Thumb Compound actions on shared state must be made atomic (i.e., read-modify-write, check-then-act) Combining individually atomic actions does not result in an atomic action All access to shared state must be synchronized, not just modifications Use same lock wherever a specific variable is accessed Variable is considered to be guarded by the specific lock Also true if multiple variables make up a single invariant You should clearly document which locks are used to guard which state Only guard mutable state that is potentially accessed by multiple threads

Locking Rules of Thumb Need right amount of locking

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock Too coarse grained could result in poor performance

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock Too coarse grained could result in poor performance Too fine grained increases complexity and could also result in poor performance

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock Too coarse grained could result in poor performance Too fine grained increases complexity and could also result in poor performance Prefer simplicity over performance Optimize later, if necessary

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock Too coarse grained could result in poor performance Too fine grained increases complexity and could also result in poor performance Prefer simplicity over performance Optimize later, if necessary Avoid holding locks during lengthy computations

Locking Rules of Thumb Need right amount of locking Too little could result in invalidate states Too much could result in deadlock Too coarse grained could result in poor performance Too fine grained increases complexity and could also result in poor performance Prefer simplicity over performance Optimize later, if necessary Avoid holding locks during lengthy computations Avoid calling external code while holding locks

Thread-Safety Example @ThreadSafe public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastnumber, lastfactors[]; @GuardedBy("this") private long hits, cachehits; public synchronized long gethits() { return hits; public synchronized double getcachehitratio() { return (double) cachehits / (double) hits; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = null; synchronized (this) { ++hits; if (i.equals(lastnumber)) { ++cachehits; factors = lastfactors.clone(); if (factors == null) { factors = factor(i); synchronized (this) { lastnumber = i; lastfactors = factors.clone(); encodeintoresponse(resp, factors);

Thread-Safety Example @ThreadSafe public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastnumber, lastfactors[]; @GuardedBy("this") private long hits, cachehits; public synchronized long gethits() { return hits; public synchronized double getcachehitratio() { return (double) cachehits / (double) hits; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = null; synchronized (this) { ++hits; if (i.equals(lastnumber)) { ++cachehits; factors = lastfactors.clone(); if (factors == null) { Is this class good now? factors = factor(i); synchronized (this) { lastnumber = i; lastfactors = factors.clone(); encodeintoresponse(resp, factors);

Thread-Safety Example @ThreadSafe public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastnumber, lastfactors[]; @GuardedBy("this") private long hits, cachehits; public synchronized long gethits() { return hits; public synchronized double getcachehitratio() { return (double) cachehits / (double) hits; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = null; synchronized (this) { ++hits; if (i.equals(lastnumber)) { Yes. The compound actions are appropriately guarded in such a way that still allows for concurrency. ++cachehits; factors = lastfactors.clone(); if (factors == null) { factors = factor(i); synchronized (this) { lastnumber = i; lastfactors = factors.clone(); encodeintoresponse(resp, factors);

Thread-Safety Example @ThreadSafe public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastnumber, lastfactors[]; @GuardedBy("this") private long hits, cachehits; public synchronized long gethits() { return hits; public synchronized double getcachehitratio() { return (double) cachehits / (double) hits; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = null; synchronized (this) { ++hits; if (i.equals(lastnumber)) { ++cachehits; factors = lastfactors.clone(); Why do we no longer use AtomicLong variables? if (factors == null) { factors = factor(i); synchronized (this) { lastnumber = i; lastfactors = factors.clone(); encodeintoresponse(resp, factors);

Thread-Safety Example @ThreadSafe public class CachedFactorizer implements Servlet { @GuardedBy("this") private BigInteger lastnumber, lastfactors[]; @GuardedBy("this") private long hits, cachehits; public synchronized long gethits() { return hits; public synchronized double getcachehitratio() { return (double) cachehits / (double) hits; public void service(servletrequest req, ServletResponse resp) { BigInteger i = extractfromrequest(req); BigInteger[] factors = null; synchronized (this) { ++hits; if (i.equals(lastnumber)) { ++cachehits; factors = lastfactors.clone(); Not necessary since access is already guarded. if (factors == null) { factors = factor(i); synchronized (this) { lastnumber = i; lastfactors = factors.clone(); encodeintoresponse(resp, factors);