Difference between revisions of "Atomicity"
Line 12: | Line 12: | ||
: [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html#compute-K-java.util.function.BiFunction- compute] | : [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html#compute-K-java.util.function.BiFunction- compute] | ||
− | =Code To Investigate= | + | =Song to Recall= |
+ | |||
+ | : ''get then put is not atomic<br>call [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction- compute]<br>call [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction- compute]<br>use [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html ConcurrentHashMap]<br>use [https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html ConcurrentHashMap]<br>or say shoot!<br>or say shoot!'' | ||
+ | |||
+ | |||
+ | =SuspectWordCount= | ||
+ | fix the code to remove the atomicity race. | ||
+ | |||
+ | =StockPortfolio= | ||
+ | =Course= | ||
+ | =Code To Fix= | ||
+ | {{Warning|Do NOT use synchronized here. ConcurrentMaps are already thread safe.}} | ||
+ | |||
+ | ==Word Count== | ||
+ | {{CodeToDebug|SuspectWordCount|countWords|atomicity.wordcount.studio}} | ||
+ | {{Parallel|public static Map<String, Integer> countWords(Iterable<String> words)}} | ||
+ | |||
+ | ==Transfer== | ||
+ | For this part of the studio we are presented with broken code. Despite using a thread-safe concurrent map: | ||
+ | |||
+ | <nowiki>private final ConcurrentMap<String, Integer> map;</nowiki> | ||
+ | |||
+ | it suffers from an atomicity race: | ||
+ | |||
+ | <nowiki>private int transfer(String listingSymbol, int deltaShareCount) { | ||
+ | Integer oldValue = this.map.get(listingSymbol); | ||
+ | Integer newValue; | ||
+ | if (oldValue != null) { | ||
+ | newValue = oldValue + deltaShareCount; | ||
+ | } else { | ||
+ | newValue = deltaShareCount; | ||
+ | } | ||
+ | this.map.put(listingSymbol, newValue); | ||
+ | return newValue; | ||
+ | }</nowiki> | ||
+ | |||
+ | {{CodeToImplement|StockPortfolio|transfer|atomicity.stockportfolio.studio}} | ||
+ | |||
+ | {{ThreadSafe|private int transfer(String listingSymbol, int deltaShareCount)}} | ||
+ | |||
+ | NOTE: do not be concerned about ConcurrentMap vs ConcurrentHashMap. The supplier's get method will return an instance of a thread-safe map. (It will, in fact, be an instance of ConcurrentHashMap.) | ||
+ | |||
+ | ==Code To Investigate== | ||
{{Warning|Although the code below is technically thread-safe, it offers check-then-act usage which leads to atomicity races.}} | {{Warning|Although the code below is technically thread-safe, it offers check-then-act usage which leads to atomicity races.}} | ||
Line 49: | Line 91: | ||
}</nowiki> | }</nowiki> | ||
− | =Code To Implement= | + | ==Code To Implement== |
We will build a thread-safe <code>Course</code> class with methods which do not lead clients towards atomicity races. | We will build a thread-safe <code>Course</code> class with methods which do not lead clients towards atomicity races. | ||
Line 66: | Line 108: | ||
NOTE: Must be thread safe. | NOTE: Must be thread safe. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=Testing Your Solution= | =Testing Your Solution= | ||
==Correctness== | ==Correctness== | ||
{{TestSuite|AtomicityTestSuite|atomicity}} | {{TestSuite|AtomicityTestSuite|atomicity}} |
Revision as of 15:48, 8 April 2020
Contents
Motivation
Many race conditions can be prevented by proper encapsulation avoiding check-then-write and read-then-modify-then-write in non-atomically.
Background
Take a look at the synchronized and lock sections of the reference page
Song to Recall
- get then put is not atomic
call compute
call compute
use ConcurrentHashMap
use ConcurrentHashMap
or say shoot!
or say shoot!
SuspectWordCount
fix the code to remove the atomicity race.
StockPortfolio
Course
Code To Fix
Warning:Do NOT use synchronized here. ConcurrentMaps are already thread safe. |
Word Count
class: | SuspectWordCount.java | |
methods: | countWords | |
package: | atomicity.wordcount.studio | |
source folder: | student/src/main/java |
method: public static Map<String, Integer> countWords(Iterable<String> words)
(parallel implementation required)
Transfer
For this part of the studio we are presented with broken code. Despite using a thread-safe concurrent map:
private final ConcurrentMap<String, Integer> map;
it suffers from an atomicity race:
private int transfer(String listingSymbol, int deltaShareCount) { Integer oldValue = this.map.get(listingSymbol); Integer newValue; if (oldValue != null) { newValue = oldValue + deltaShareCount; } else { newValue = deltaShareCount; } this.map.put(listingSymbol, newValue); return newValue; }
class: | StockPortfolio.java | |
methods: | transfer | |
package: | atomicity.stockportfolio.studio | |
source folder: | student/src/main/java |
method: private int transfer(String listingSymbol, int deltaShareCount)
(thread-safe required)
NOTE: do not be concerned about ConcurrentMap vs ConcurrentHashMap. The supplier's get method will return an instance of a thread-safe map. (It will, in fact, be an instance of ConcurrentHashMap.)
Code To Investigate
Warning:Although the code below is technically thread-safe, it offers check-then-act usage which leads to atomicity races. |
class: | CheckThenActCourse.java | DEMO: |
methods: | isSpaceRemaining add drop |
|
package: | atomicity.course.studio | |
source folder: | src//java |
public class CheckThenActCourse { private final Collection<Student> students; private final int limit; public CheckThenActCourse(int limit, Supplier<Collection<Student>> collectionSupplier) { this.students = collectionSupplier.get(); this.limit = limit; } public int getLimit() { return this.limit; } public boolean isSpaceRemaining() { synchronized (this.students) { return this.students.size() < this.limit; } } public void add(Student student) { synchronized (this.students) { this.students.add(student); } } public boolean drop(Student student) { synchronized (this.students) { return this.students.remove(student); } } }
Code To Implement
We will build a thread-safe Course
class with methods which do not lead clients towards atomicity races.
class: | Course.java | |
methods: | addIfSpace drop |
|
package: | atomicity.course.studio | |
source folder: | student/src/main/java |
method: public boolean addIfSpace(Student student)
(thread-safe required)
Adds the student if there is space under the limit specified to the constructor and returns whether or not the add actually occurred.
NOTE: Must be thread safe.
method: public boolean drop(Student student)
(thread-safe required)
Removes the student from the course if the student was enrolled (the remove method returns just the status you need).
NOTE: Must be thread safe.
Testing Your Solution
Correctness
class: | AtomicityTestSuite.java | |
package: | atomicity | |
source folder: | testing/src/test/java |