Pseudo-code and questions for a thread safe code

User Generated

Xhbyhb

Programming

Description

The introduction of the program is in the .pdf and the relevant code provided is in the .zip. Moreover, the class diagram is in the figure. The final goal is to complete the code provided but for this mission includes only documentation and class specifications. (The introduction for how to write class specifications is in the .ppt.

1st Deliverable: Pseudo-code, invariants, pre- and post-conditions for the AuctionServer and Simulation classes.

Unformatted Attachment Preview

CSYE7215: Homework 2: Due date: Pseudo-code and Questions: September, 2018, 05:59 pm Completed project: September 27, 2018; 05:59 pm Goal: In this project you will simulate an auction service in which sellers can offer items and bidders can bid on them. In particular, you will implement a class, AuctionServer, in Java, that provides methods supporting all aspects of an auction. Because many sellers and bidders will be interacting at the same time, the program should be thread-safe. Additionally, the requirement for this assignment is to inject unfairness (or bias) in the behavior of AuctionServer. This means that AuctionServer would give some preferential treatment to some of the buyers (not sellers). You will have to come up with a strategy which you will need to document in your submissions. Restrictions. The focus of this project is to learn how to write thread-safe code. For this reason, you are not allowed to use any concurrent collections in the Java libraries. Specifically, do not use anything contained in java.util.concurrent or the synchronized collections in the Collections class in java.lang.Object. You may use the regular (i.e. non-synchronized) version of collections if you wish, however. Model: The auction service follows a basic client/server model. There are two types of clients:  Sellers: o Can submit new items to the server  Bidders: o Can request a listing of current items o Can check the price of an item o Can place a bid on an item o Can check the outcome of a bid These actions will be implemented as methods in the AuctionServer. Rules: There are a number of restrictions that must be enforced with regard to listing and bidding:  Listing: o Sellers will be limited to having maxSellerItems different active items at any given time o The AuctionServer will have a limit, serverCapacity, for the number of total items offered from all Sellers o Bidding: o New bids must at least match the opening bid if no one else has bid yet, OR exceed the current highest bid if other bids have already been placed on the item o Bidders will be limited to having maxBidCount active bids on different current items o Once a Bidder holds the current highest bid for an item they will only be allowed to successfully place another bid if another Bidder overtakes them for the current highest bid. Assumptions: The following assumptions must hold about the AuctionServer:   Listing: o All prices will be listed in whole dollars only o All items will open with non-negative opening price not exceeding $100 o If an auction expires with no bids placed, the Seller will not re-list the item and the server receives no profit from it Bidding: o Items can receive any number of bids as long as the auction has not expired o Once a bid has been placed it cannot be retracted o A single Bidder cannot place more than one bid at a single moment Bidder/Seller behavior: The Bidder and Seller client classes have been implemented for you. You'll note that they both implement the Client interface that is also provided. Also an Item class is provided. Many clients will run in different threads, and will all access the singleton instance AuctionServer which you will be implementing. The lifecycle of the test program follows the following three stages (see the UML diagram): 1. Create several clients and execute them on multiple threads 2. Wait for all of the clients to finish 3. Verify the correctness of the system state based on information given below Seller clients will behave in the following way: • They will hold a list of items to sell (ours generates a large number of items at random to try to sell). • They are initialized with things such as a unique name and how many cycles it should execute and the longest it should sleep between attempts to list an item. • When the thread is run it enters a loop and iterates the specified number of cycles, where in each cycle it will o o o randomly pick an item and try to submit it to the server, then if the submission is accepted, that item is then removed from its own list of things it wants to sell, and then after each submission attempt to the server, the Seller sleeps for some random amount of time Bidder clients will behave in the following way: • They are initialized with things such as a unique name and how much cash it has available to spend, how many cycles it should TRY to execute (if it's out of cash, why bother continuing) and the longest it should sleep between attempts to buy and check items. • When the thread is run it enters a loop and iterates the specified number of cycles (though there is an escape clause for if it runs out of cash) where in each cycle it will o try to buy things and o check up on all of its active bids • To try to buy things within each cycle, it will o retrieve a list of items available for sale, then o randomly picks an item that it can afford going on the worst-­­case assumption that it wins all outstanding bids, and o adds $1 to the highest bidding price and makes the bid • To check up on all of its active bids within each cycle, it will o check the status of the bid and o if the bid was successful (i.e., it won the auction) it will deduct the price of that item from its cash reserve. Note that these classes do not enforce the restrictions listed earlier in the “Rules” section. This is your job to implement in the AuctionServer. Code to implement: For this project you are only required to modify one file, AuctionServer.java. The methods to implement will start with the comment // TODO: IMPLEMENT CODE HERE. A description of each of the methods follows below. Refer to the comments for each method for further notes on what these methods should do. • submitItem(...) A Seller calls this method to submit an item to be listed by the AuctionServer. A Seller uses sellerName and itemName to identify itself and the Item that is submitted. The unit for the bidding duration is in milliseconds. If the Item can be successfully placed, this method returns a unique positive listing ID generated by the AuctionServer. If the Item cannot be placed, for instance, the Seller has already used up its quota or the server has reached maxSellerItems items listed, this method returns -1. • getItems() A Bidder calls this method to retrieve a copy of the list of Items currently listed as active. Each Item object in the list provides access to its name and its initial minimum bidding price. (It is important to remember the current bid price of the item may have changed from its initial value and the actual bid price can be retrieved by calling the method itemPrice(), see below.) • itemPrice(...) A Bidder checks the current bid/opening price for an Item by supplying the unique listing ID of that Item. The value returned by this method is the highest bid made so far, or the minimum bid value supplied by the seller if nobody made a bid on the item. If there is no Item with the supplied listing ID the method indicates an error by returning a value of -1. • itemUnbid(...) A Bidder checks whether an Item has not yet been bid upon by supplying the unique listing ID of that Item. This method returns true if no bid has been placed and false otherwise. If there is no Item with the supplied listing ID the method returns a value of true since it is true that the non-existing Item has not yet been successfully bid upon. • submitBid(...) A Bidder calls this method to submit a bid for a listed Item. This method returns true if the bid is successfully submitted and false if the submission request is rejected. There are several situations when a bid submission request can be rejected. If a Bidder already has bid on too many items, the Bidder is not allowed to place bids on new items. If a Bidder already has a bid on an item, the Bidder is not allowed to place a new bid on the same item until another Bidder has placed a higher bid. The bid can also be rejected if the item is no longer for sale, or if the Bidder has been added to the blacklist due to violation of a Rule, or if the listing ID corresponds to none of the items submitted by the sellers. • checkBidStatus(...) A Bidder calls this method to poll the AuctionServer to check the status of a bid the Bidder may have on an Item. There are three possible status results: 1. SUCCESS (return value 1): If this item's bidding duration has passed and the Bidder has the highest bid. 2. OPEN (return value 2): If this item is still receiving bids. 3. FAILED (return value 3): If bidding is over and this Bidder did not win, or if the listing ID doesn't correspond to any Item submitted by the sellers. As part of its job, if the item being checked is no longer open and still appears on the list of items currently listed as active, this method will remove it from that list and update the appropriate locations to reflect that it is no longer being bid upon and also update the appropriate fields if it was successfully sold to anyone. Fields: The limits mentioned earlier in the description are all stored as public constant integers. Please note that we can change these values as part of our grading but we will not change the names of the constants. They will be:  an integer constant called maxBidCount  an integer constant called maxSellerItems  an integer constant called serverCapacity You will also be required to keep track of two statistics for the AuctionServer’s sales:  a mutable integer called soldItemsCount (the total number of items per 100 milliseconds that have been sold so far; this is like a selling rate)  a mutable integer called revenue (the revenue generated so far) These values are required to stay up-to-date and also be thread-safe. Testing: It is important to check your program’s performance thoroughly. The class Simulation.java has been provided for you to perform just the basic testing. By default, the main method simply creates Sellers and Bidders and runs them on the AuctionServer. You may modify the Simulation.java class as you see fit. Sharing of tests for this project is encouraged. After all, even though your program may pass your tests, it may fail on someone else’s test. Since this assignment requires that your ActionServer is biased, you need to provide some code in the Simulation class that will analyze this aspect, i.e., it will show that AuctionServer behaves according to the bias policy that you declare and implement. In other words, your Simulation should expose the advantage received by the buyer you chose to give preferential treatment to. For this purpose, you need to propose and implement the statistic that will show how much that seller benefited due to the server’s bias as opposed to other sellers. Note that the bias doed not need to be caught in any single run, but it should come out in a number of runs. Submission: Use your Last Name (jut one word; you can simplify your last name if you prefer) for the package name. Submit a .zip file containing your project files to Assignments in Blackboard. 1st Deliverable: Pseudo-code, invariants, pre- and post-conditions for the AuctionServer and Simulation classes. 2nd Deliverable (Final): Code for AuctionServer and Simulation. CSYE7215: Homework 2: Due date: Pseudo-code and Questions: September, 2018, 05:59 pm Completed project: September 27, 2018; 05:59 pm Goal: In this project you will simulate an auction service in which sellers can offer items and bidders can bid on them. In particular, you will implement a class, AuctionServer, in Java, that provides methods supporting all aspects of an auction. Because many sellers and bidders will be interacting at the same time, the program should be thread-safe. Additionally, the requirement for this assignment is to inject unfairness (or bias) in the behavior of AuctionServer. This means that AuctionServer would give some preferential treatment to some of the buyers (not sellers). You will have to come up with a strategy which you will need to document in your submissions. Restrictions. The focus of this project is to learn how to write thread-safe code. For this reason, you are not allowed to use any concurrent collections in the Java libraries. Specifically, do not use anything contained in java.util.concurrent or the synchronized collections in the Collections class in java.lang.Object. You may use the regular (i.e. non-synchronized) version of collections if you wish, however. Model: The auction service follows a basic client/server model. There are two types of clients:  Sellers: o Can submit new items to the server  Bidders: o Can request a listing of current items o Can check the price of an item o Can place a bid on an item o Can check the outcome of a bid These actions will be implemented as methods in the AuctionServer. Rules: There are a number of restrictions that must be enforced with regard to listing and bidding:  Listing: o Sellers will be limited to having maxSellerItems different active items at any given time o The AuctionServer will have a limit, serverCapacity, for the number of total items offered from all Sellers o Bidding: o New bids must at least match the opening bid if no one else has bid yet, OR exceed the current highest bid if other bids have already been placed on the item o Bidders will be limited to having maxBidCount active bids on different current items o Once a Bidder holds the current highest bid for an item they will only be allowed to successfully place another bid if another Bidder overtakes them for the current highest bid. Assumptions: The following assumptions must hold about the AuctionServer:   Listing: o All prices will be listed in whole dollars only o All items will open with non-negative opening price not exceeding $100 o If an auction expires with no bids placed, the Seller will not re-list the item and the server receives no profit from it Bidding: o Items can receive any number of bids as long as the auction has not expired o Once a bid has been placed it cannot be retracted o A single Bidder cannot place more than one bid at a single moment Bidder/Seller behavior: The Bidder and Seller client classes have been implemented for you. You'll note that they both implement the Client interface that is also provided. Also an Item class is provided. Many clients will run in different threads, and will all access the singleton instance AuctionServer which you will be implementing. The lifecycle of the test program follows the following three stages (see the UML diagram): 1. Create several clients and execute them on multiple threads 2. Wait for all of the clients to finish 3. Verify the correctness of the system state based on information given below Seller clients will behave in the following way: • They will hold a list of items to sell (ours generates a large number of items at random to try to sell). • They are initialized with things such as a unique name and how many cycles it should execute and the longest it should sleep between attempts to list an item. • When the thread is run it enters a loop and iterates the specified number of cycles, where in each cycle it will o o o randomly pick an item and try to submit it to the server, then if the submission is accepted, that item is then removed from its own list of things it wants to sell, and then after each submission attempt to the server, the Seller sleeps for some random amount of time Bidder clients will behave in the following way: • They are initialized with things such as a unique name and how much cash it has available to spend, how many cycles it should TRY to execute (if it's out of cash, why bother continuing) and the longest it should sleep between attempts to buy and check items. • When the thread is run it enters a loop and iterates the specified number of cycles (though there is an escape clause for if it runs out of cash) where in each cycle it will o try to buy things and o check up on all of its active bids • To try to buy things within each cycle, it will o retrieve a list of items available for sale, then o randomly picks an item that it can afford going on the worst-­­case assumption that it wins all outstanding bids, and o adds $1 to the highest bidding price and makes the bid • To check up on all of its active bids within each cycle, it will o check the status of the bid and o if the bid was successful (i.e., it won the auction) it will deduct the price of that item from its cash reserve. Note that these classes do not enforce the restrictions listed earlier in the “Rules” section. This is your job to implement in the AuctionServer. Code to implement: For this project you are only required to modify one file, AuctionServer.java. The methods to implement will start with the comment // TODO: IMPLEMENT CODE HERE. A description of each of the methods follows below. Refer to the comments for each method for further notes on what these methods should do. • submitItem(...) A Seller calls this method to submit an item to be listed by the AuctionServer. A Seller uses sellerName and itemName to identify itself and the Item that is submitted. The unit for the bidding duration is in milliseconds. If the Item can be successfully placed, this method returns a unique positive listing ID generated by the AuctionServer. If the Item cannot be placed, for instance, the Seller has already used up its quota or the server has reached maxSellerItems items listed, this method returns -1. • getItems() A Bidder calls this method to retrieve a copy of the list of Items currently listed as active. Each Item object in the list provides access to its name and its initial minimum bidding price. (It is important to remember the current bid price of the item may have changed from its initial value and the actual bid price can be retrieved by calling the method itemPrice(), see below.) • itemPrice(...) A Bidder checks the current bid/opening price for an Item by supplying the unique listing ID of that Item. The value returned by this method is the highest bid made so far, or the minimum bid value supplied by the seller if nobody made a bid on the item. If there is no Item with the supplied listing ID the method indicates an error by returning a value of -1. • itemUnbid(...) A Bidder checks whether an Item has not yet been bid upon by supplying the unique listing ID of that Item. This method returns true if no bid has been placed and false otherwise. If there is no Item with the supplied listing ID the method returns a value of true since it is true that the non-existing Item has not yet been successfully bid upon. • submitBid(...) A Bidder calls this method to submit a bid for a listed Item. This method returns true if the bid is successfully submitted and false if the submission request is rejected. There are several situations when a bid submission request can be rejected. If a Bidder already has bid on too many items, the Bidder is not allowed to place bids on new items. If a Bidder already has a bid on an item, the Bidder is not allowed to place a new bid on the same item until another Bidder has placed a higher bid. The bid can also be rejected if the item is no longer for sale, or if the Bidder has been added to the blacklist due to violation of a Rule, or if the listing ID corresponds to none of the items submitted by the sellers. • checkBidStatus(...) A Bidder calls this method to poll the AuctionServer to check the status of a bid the Bidder may have on an Item. There are three possible status results: 1. SUCCESS (return value 1): If this item's bidding duration has passed and the Bidder has the highest bid. 2. OPEN (return value 2): If this item is still receiving bids. 3. FAILED (return value 3): If bidding is over and this Bidder did not win, or if the listing ID doesn't correspond to any Item submitted by the sellers. As part of its job, if the item being checked is no longer open and still appears on the list of items currently listed as active, this method will remove it from that list and update the appropriate locations to reflect that it is no longer being bid upon and also update the appropriate fields if it was successfully sold to anyone. Fields: The limits mentioned earlier in the description are all stored as public constant integers. Please note that we can change these values as part of our grading but we will not change the names of the constants. They will be:  an integer constant called maxBidCount  an integer constant called maxSellerItems  an integer constant called serverCapacity You will also be required to keep track of two statistics for the AuctionServer’s sales:  a mutable integer called soldItemsCount (the total number of items per 100 milliseconds that have been sold so far; this is like a selling rate)  a mutable integer called revenue (the revenue generated so far) These values are required to stay up-to-date and also be thread-safe. Testing: It is important to check your program’s performance thoroughly. The class Simulation.java has been provided for you to perform just the basic testing. By default, the main method simply creates Sellers and Bidders and runs them on the AuctionServer. You may modify the Simulation.java class as you see fit. Sharing of tests for this project is encouraged. After all, even though your program may pass your tests, it may fail on someone else’s test. Since this assignment requires that your ActionServer is biased, you need to provide some code in the Simulation class that will analyze this aspect, i.e., it will show that AuctionServer behaves according to the bias policy that you declare and implement. In other words, your Simulation should expose the advantage received by the buyer you chose to give preferential treatment to. For this purpose, you need to propose and implement the statistic that will show how much that seller benefited due to the server’s bias as opposed to other sellers. Note that the bias doed not need to be caught in any single run, but it should come out in a number of runs. Submission: Use your Last Name (jut one word; you can simplify your last name if you prefer) for the package name. Submit a .zip file containing your project files to Assignments in Blackboard. 1st Deliverable: Pseudo-code, invariants, pre- and post-conditions for the AuctionServer and Simulation classes. 2nd Deliverable (Final): Code for AuctionServer and Simulation. Thread Anomalies • Scheduler determines when threads execute – Thread computation can be interleaved on a single processor, or – Threads computations can be on different processors, or – Some combination of both • Programmer can have some influence via yield(), setPriority(), etc. • But most decisions are outside user control, leading to possibilities for – Nondeterminism – Interference: threads overwrite each other’s work 2/5/2014 ©2012-14 University of Maryland 1 Anomaly from Lecture 2 • IncThread.java public class IncThread implements Runnable { private static int shared = 0; // Shared variable private String name = ""; // Name of thread IncThread (String name) { this.name = name; } public void run () { int myShared = shared; System.out.println (name + " read shared = " + myShared); myShared++; shared = myShared; System.out.println (name + " assigned to shared: " + myShared); } } • Main thread IncRace created two instances, t1 and t2, and started both 2/5/2014 ©2012-14 University of Maryland 2 Two Threads t1 t2 myShared = shared; myShared = shared; print myShared; print myShared; myShared++; myShared++; shared = myShared; shared = myShared; print myShared; print myShared; • Different schedules can leave shared = 2, shared = 1 • This is an example of a data race 2/5/2014 ©2012-14 University of Maryland 3 Data Races and Race Conditions • A data race occurs (may occur) when the same memory location can be accessed simultaneously by two threads, with at least one of accesses a write. • They “seem bad” … but why? – In previous example, if it does not matter if shared is 1 or 2, then is there an error? – On the other hand, if shared should only be 2, then there is an error. • A race condition may occur when a program’s correctness depends on scheduling decisions – If the correct outcome of the previous example is shared == 2, then the data race induces a race condition – If the correct outcome is shared == 1 or shared == 2, then there is no race condition! 2/5/2014 ©2012-14 University of Maryland 4 Correctness? • Definition of race condition mentions program correctness • We will adopt a class-based view: A class is correct if it satisfies its specification • So what is a “class specification”? 2/5/2014 ©2012-14 University of Maryland 5 Class Specifications • • • • • Classes are used to define objects Classes contain static members Objects contain instance members Some members are fields, while others are methods Classes generally enforce consistency constraints on static, instance members – Field values should be “consistent” – Methods should preserve consistency, compute the right thing • To experiment with static play with StaticTest (L1) 2/5/2014 ©2012-14 University of Maryland 6 Example: Point and Line Classes • Point.java public class Point { private final double x; private final double y; Point (double x, double y) { this.x = x; this.y = y; } double getX () { return x; } double getY () { return y; } } • Line.java public class Line { private Point p1; private Point p2; Line (Point p1, Point p2) { this.p1 = p1; this.p2 = p2; } public double slope () { return ((p1.getY() - p2.getY()) / (p1.getX() - p2.getX())); } } 2/5/2014 ©2012-14 University of Maryland 7 Notions of Consistency for Line? • Would like to know that points are different! • Invariants capture notion of consistency – Invariants describe properties that must always hold among instance variables – They reflect relationships you can “rely on” • Here is an invariant for Line: p1 and p2 must be different points • Is Line class correct? No! – Constructor does not check that points are different – So constructor can construct objects violating invariant 2/5/2014 ©2012-14 University of Maryland 8 Corrected Line Class • CorrectLine.java – change constructor to: CorrectLine (Point p1, Point p2) throws IllegalArgumentException { if ( (p1.getX() != p2.getX()) || (p1.getY() != p2.getY()) ) { this.p1 = p1; this.p2 = p2; } else { throw new IllegalArgumentException ( "Points to Line constructor must differ: “ + p1.toString() + "given twice."); } } • Note that when invariant violation is detected, no updating is performed, and exception is thrown! 2/5/2014 ©2012-14 University of Maryland 9 Is the CorrectLine Class Correct? • Some would say yes … • … and yet there is one more issue: division by zero! – If p1, p2 have the same x-value, then the slope calculation involves dividing by 0 – This can throw a run-time exception! • This is not a consistency issue among fields, but instead a property of methods. 2/5/2014 ©2012-14 University of Maryland 10 Class Specifications: Preconditions / Postconditions / Exception Conditions • To specify the behavior of methods, need – Preconditions: what should hold of inputs, fields in order to ensure correct termination – Postconditions: what will hold when method exits normally – Exceptions: what happens when precondition violated • In case of slope method … – Specification should indicate that if points form a vertical line, then method will throw an exception; otherwise, slope is returned – Header for method should be changed to reflect this 2/5/2014 ©2012-14 University of Maryland 11 Corrected slope() Method • CorrectedLine.java // Precondition: p1, p2 do not form vertical line // Postcondition: return slope of line thru p1, p2 // Exception: if p1, p2 form vertical line, throw // ArithmeticException public double slope () throws ArithmeticException { return ((p1.getY() - p2.getY()) / (p1.getX() p2.getX())); } 2/5/2014 ©2012-14 University of Maryland 12 Class Specifications • Invariants on fields • Preconditions / postconditions / exceptions for all methods! – Put this in documentation – Ongoing research (“formal methods”) on better support for this 2/5/2014 ©2012-14 University of Maryland 13 Class Correctness • When is a class correct with respect to a specification? – The fields always satisfy the invariant (except when a method is in the middle of executing) – Each method produces results consistent with the postcondition when started with inputs / field values satisfying the precondition – Each method produces results consistent with the exception condition when started with inputs / field values violating the precondition • The Line class is not correct for the given specification, while CorrectLine is! 2/5/2014 ©2012-14 University of Maryland 14 Establishing Correctness in the Sequential Case • Check that each constructor returns an object satisfying the invariant • Check that each method leaves the invariant true if it starts with the invariant true • Check preconditions / postcondition / exceptions • Works because of validity of procedural abstraction! – Method call can be viewed as one atomic operation that is equivalent to executing body of method – So analyzing correctness can be done on a method-bymethod basis 2/5/2014 ©2012-14 University of Maryland 15 Problems with Threads • Even if a class is correct with respect to a specification, threads can break invariants! • This happens because: – A class can be correct even though methods might break the invariants in the middle of their execution Methods only have to make sure the invariants hold when they terminate. – Concurrency breaks procedural abstraction! • One thread can see the intermediate results of another thread’s execution • If the second thread is in the middle of a method call, the class’s invariants might not be true • The first thread then gets an inconsistent view of the corresponding object 2/5/2014 ©2012-14 University of Maryland 16 Example: IncThread Revisited • IncThread.java … private static int shared = 0; // Shared variable … public void run () { int myShared = shared; myShared++; shared = myShared; • Specification – Invariant: shared records the number of times run() has been invoked – Precondition / postcondition / exception for run(): no requirements • IncThread is correct (sequentially)! – Initially, invariant is true, since shared == 0 – run() increments shared, so invariant is true when run() finishes if it is true when run() starts • There are erroneous runs when there are multiple threads! – Until run() increments shared invariant is not true – Another thread can then read an inconsistent value of shared! 2/5/2014 ©2012-14 University of Maryland 17 Thread Safety A correct class is thread-safe if every execution of any threaded application using the class preserves the specification’s invariants and method specifications – Thread safety only makes sense if you have a class specification! – This fact is crucial but often overlooked 2/5/2014 ©2012-14 University of Maryland 18 Example Re-revisited • Suppose IncThread invariant is changed to: The value of shared is ≤ the number of times run() is executed • Then IncThread is thread-safe! – Every value any thread might read of shared is ≤ the number of times run() has been invoked – Every thread increments shared – Even though there is a data race, the class can be used as is in a threaded application, for this specification • Again: thread-safety is a property of a class and its specification, not just of a class 2/5/2014 ©2012-14 University of Maryland 19 Recap • A class can be correct with respect to its specification and still not be thread-safe • Why? – The methods in a correct class will preserve the specifications invariants before and after each executes – During execution of a method, the invariants might not be true – In a multi-threaded application, another thread might see this inconsistent state of an object, since procedural abstraction is violated! • Implication: if a class is not thread-safe, it cannot be counted on to be correct in a multi-threaded execution 2/5/2014 ©2012-14 University of Maryland 20 Fixing Thread Safety Problems • Thread-safety is guaranteed for immutable objects – In immutable objects, the fields never change after construction – So if the fields of an object satisfy an invariant after it is built, it will never violate the invariant • Rule of thumb: when feasible, use immutable objects 2/5/2014 ©2012-14 University of Maryland 21 Implementing Points • Immutable: Point.class public class Point { private final double x; private final double y; Point (double x, double y) { this.x = x; this.y = y; } For any specification of Point, if Point is correct then it is thread-safe! • Mutable: MutablePoint.class public class MutablePoint { private double x; private double y; MutablePoint (double x, double y) { this.x = x; this.y = y; } Depending on other operations, specification, this class may not be thread safe (e.g. if there are setters as well as getters) 2/5/2014 ©2012-14 University of Maryland 22 Fixing Thread-Safety Problems: Locks • Thread-safety problems are often related to methods inducing invariant errors while “in flight” – The invariant errors are fixed before the method terminates – If another thread sees this intermediate erroneous data, it can use it without realizing it. • The issue: procedural abstraction – We would like to think of method calls as atomic, i.e. as either not having started or having finished, like single machine instructions – This perspective is valid in a sequential program – It is not in a multi-threaded program • A solution: use locks to give illusion of atomicity! 2/5/2014 ©2012-14 University of Maryland 23 Lock Fundamentals • Examples of a concurrency-control primitive – As the name suggests, concurrency-control primitives are intended to control concurrency! – The idea: eliminate the possibility of concurrency while critical operations are taking place • A lock is a data structure – Two states: locked, unlocked – Two operations: acquire, release • acquire: block execution until the state of the lock is unlocked, then set state to locked • release: set status of lock to unlocked • Both operations are atomic • Variations: – Releasing a lock whose status is unlocked may or may not throw an exception – Some locks have more states (e.g. read-locked) 2/5/2014 ©2012-14 University of Maryland 24 Using Locks to Fix Thread-Safety Issues • Idea – Associate lock(s) with classes – Methods must acquire appropriate locks before performing internal operations that may violate invariants – Methods release locks when invariant is restored • This ensures that multiple threads cannot see intermediate changes that methods make to fields during execution! 2/5/2014 ©2012-14 University of Maryland 25 Locks in Java • Several types – Intrinsic / monitor locks – Various classes whose objects are locks • We will first study intrinsic / monitor locks (both terms are used) 2/5/2014 ©2012-14 University of Maryland 26 Intrinsic / Monitor Locks • Every object in Java has a lock associated with it, called the monitor (lock) or intrinsic lock • No explicit acquire / release operations; rather the state of an intrinsic lock is modified using synchronized blocks – Basic form: synchronized (obj) { statements } – Semantics • Acquire intrinsic lock of obj • Execute statements • Release intrinsic lock of obj when block exits (terminates, throws an exception, breaks, etc.) 2/5/2014 ©2012-14 University of Maryland 27 Fixing IncRace.java • SyncIncThread.java public class SyncIncThread implements Runnable { private static int shared = 0; static Object lock = new Object (); // Lock must be static! … public void run () { synchronized (lock) { int myShared = shared; myShared++; shared = myShared; } } } • • • • The specification invariant is that shared is the number of invocations of run(). The class-wide (static) object lock is used to “guard” the part of run() where the invariant is violated (i.e. where shared is not yet updated). “static” guarantees that all threads use the same lock. When one thread is executing its synchronized block, all other threads are waiting outside their synchronized blocks After run updates shared the invariant has been restored, and the lock can be released. 2/5/2014 ©2012-14 University of Maryland 28 Synchronized Instance Methods • In many cases we want entire methods to occur atomically • Java provides the following short-hand for this by allowing methods to be declared synchronized – E.g. public synchronized void setP1 (Point p1) { this.p1 = p1; } – This is an abbreviation for the following, since the method is an instance method public boolean setP2 (Point p2) { synchronized (this) { this.p2 = p2; } } 2/5/2014 ©2012-14 University of Maryland 29 Synchronized Static Methods • Static (class) methods may also be synchronized – For example, could add following method to SyncIncThread public synchronized static void incShared () { ++shared; } – What object’s intrinsic lock is used in this case? – Answer: the class object associated with the relevant class! – In this case, here is equivalent code: public static void altIncShared () { synchronized (SyncIncThread.class) { ++shared; } } 2/5/2014 ©2012-14 University of Maryland 30 Reentrant Locking • Intrinsic locks are reentrant! – If a thread acquires an intrinsic lock, it can acquire it again without blocking – A thread with multiple acquisitions on an intrinsic lock frees it only when the number of releases equals the number of acquisitions • Huh? – Consider following code used to do atomic updating of a bounded counter public synchronized boolean isMaxed () { return (value == upperBound); } public synchronized void inc () { if (!isMaxed()) ++inc; } – Without reentrant locking, every call to inc() would block forever! – Because it calls isMaxed, which is also synchronized. – But since this (intrinsic) lock is re-entrant, the same thread that already holds the lock, can enter again. – In some cases this is not good (when entering the same block modifies the state). 2/5/2014 ©2012-14 University of Maryland 31 Example: Bounded Counter Class • BoundedCounter.java: a correct, but not thread-safe class. • How do we make it thread safe? 2/5/2014 ©2012-14 University of Maryland 32 BoundedCounter.java (some method specs elided) // Not thread-safe public class BoundedCounter { private int value = 0; private int upperBound = 0; //INVARIANT: in all instances 0 = 0) this.upperBound = upperBound; else throw new IllegalArgumentException ("Bad argument to BoundedCounter: " + upperBound + ";must be >= 0"); } //Precondition: none //Postcondition: current value returned //Exception: none public int current () { return value; } public void reset () { value = 0; } public boolean isMaxed () { return (value == upperBound); } //Precondition: none //Postcondition: increment value if not maxed; otherwise, do nothing. //Exception: none public void inc () { if (!isMaxed()) ++value; } } 2/5/2014 ©2012-14 University of Maryland 33 Design Considerations • Whose job is it to enforce correctness? – Class designer/implementer? Or User? – In BoundedCounter.java, could have implemented inc as: public void inc () { ++value; } • This would put burden on maintaining correctness on user  • But it may be more efficient ☺ – A better perspective • Class should enforce correctness • Class designer, though, can choose what notion of correctness is • In the inc example, invariant could be relaxed to say that only correctness criterion is 0
Purchase answer to see full attachment
User generated content is uploaded by users for the purposes of learning and should be used following Studypool's honor code & terms of service.

Explanation & Answer

Here is the eclipse...


Anonymous
Awesome! Perfect study aid.

Studypool
4.7
Trustpilot
4.5
Sitejabber
4.4

Similar Content

Related Tags