1 / 37

Threading and Concurrent Programming in Java

Threading and Concurrent Programming in Java. Synchronization D.W. Denbo. Outline. Race condition Lock objects synchronized keyword Synchronized block Volatile Fields Deadlocks Lock testing. Race Condition.

darren
Download Presentation

Threading and Concurrent Programming in Java

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Threading and ConcurrentProgramming in Java Synchronization D.W. Denbo Java Threads

  2. Outline • Race condition • Lock objects • synchronized keyword • Synchronized block • Volatile Fields • Deadlocks • Lock testing Java Threads

  3. Race Condition In most real applications, two or more threads will need to share access to the same objects. What happens if two threads have access to the same object calls a methods that changes the object state? As you might expect the threads can step on each other’s toes. Such a situation is often called a race condition. Java Threads

  4. Example of a Race Condition To avoid corruption of share data, you must learn how to synchronize the access. In this example, we have the class Bank with the method transfer. Money can be transferred between accounts. A thread is created for each account to transfer money from its account to another. Java Threads

  5. public void transfer(int from, int to, double amount) { System.out.print(Thread.currentThread()); accounts[from] -= amount; System.out.printf(“%10.2f from %d to $d”,amount,from,to); accounts[to]+= amount; System.out.printf(“Total: %10.2f%n”,getTotalBalance()); } Java Threads

  6. public void run() { try { int toAccount = (int)(bank.size()*Math.random()); double amount = maxAmount*Math.random(); bank.transfer(fromAccount, toAccount, amount); Thread.sleep((int)(DELAY*Math.random())); } catch (InterruptedException ie) { } } Java Threads

  7. UnsynchBankTest Java Threads

  8. While this simulation is running, we don’t know how much money is in any one account. We do, however, know that the total amount of money should remain constant! Java Threads

  9. What Happened? Errors have crept in and some amount of money was either lost or spontaneously created. Why? accounts[to] += amount; The problem is that these are not atomic operations! Java Threads

  10. The instructions might be processed as follows: • Load accounts[to] into a register • Add amount • Move the result back to accounts[to]. If the first thread executes Steps 1 & 2, then is interrupted by a second thread that updates the same entry, the second threads changes are lost. Java Threads

  11. Thread 2 accounts[to] Thread 1 Thread 1 register Thread 2 register 5000 5000 load 5500 add 5000 5000 5000 load 5900 5000 add 5900 5900 store 5500 5500 store

  12. Lock Objects Starting with JDK 5.0, there are two methods for protecting a code block from concurrent access. • The synchronized keyword • The ReentrantLock class We’ll first take a closer look at the ReentrantLock: Java Threads

  13. This construct guarantees that only one thread can access the critical section at a time. myLock.lock(); // a ReentrantLock object try { // critical section } finally { myLock.unlock(); // make sure lock is unlocked } Java Threads

  14. public class Bank { public void transfer(int from, int to, double amount) { bankLock.lock(); try { if(accounts[from] < amount) return; System.out.print(Thread.currentThread()); accounts[from] -= amount; System.out.printf(“%10.2f from %d to %d”,amount,from,to); accounts[to] += amount; System.out.printf(“Total: %10.2f%n”,getTotalBalance()); } finally { bankLock.unlock(); } } .... // Reentrant lock implements the Lock interface private Lock bankLock = new ReentrantLock(); } Java Threads

  15. Note that • Each Bank object has its own ReentrankLock • If two threads try to access the same Bank object, then the lock will serialize access. • If two threads try to access different Bank objects, each thread acquires a different lock and neither thread is blocked. Java Threads

  16. Condition Objects Often, a thread enters a critical section, only to discover that it can’t proceed until a condition is fulfilled. You use a conditionobject to manage threads that have acquired lock but cannot do useful work. Lets extend our example to cover the case of insufficient funds. if(bank.getBalance(from) >= amount) bank.transfer(from, to, amount); The above won’t work! Java Threads

  17. So what do we do when there isn’t enough money? We wait until some other thread puts enough money in! public void transfer(int from, int to, double amount) { bankLock.lock(); try { while(accounts[from] < amount) { // wait } // transfer funds } finally { bankLock.unlock(); } } Java Threads

  18. A lock object can have one or more associated condition objects. Here we set up a condition object to represent the “sufficient funds” condition. class Bank { public Bank() { .... sufficientFunds = bankLock.newCondition(); } .... private Condition sufficientFunds; } Java Threads

  19. public void transfer(int from, int to, double amount) { bankLock.lock(); try { while(accounts[from] < amount) sufficientFunds.await(); // transfer funds ... sufficientFunds.signalAll(); } finally { bankLock.unlock(); } } NOTE: the call to signalAll does not immediately activate a waiting thread. It only unblocks the waiting threads so that they can compete for entry into the object. Java Threads

  20. SynchBankTest Java Threads

  21. A lock protects sections of code, allowing only one thread access at a time. • A lock manages threads that are trying to enter protected code segments. • A lock can have one more associated condition objects. • Each condition object manages threads that have entered a protected code section but cannot proceed. Java Threads

  22. synchronized Keyword Every object in Java has an implicit lock. If a method is declared with the synchronized keyword, then the object’s lock protects the entire method. This implicit lock has a single implicit condition. The wait method adds a thread to the wait set, and the notifyAll/notify methods unblock the waiting threads. Java Threads

  23. class Bank { public synchronized void transfer(int from, int to, double amount) { while(accounts[from] < amount) wait(); // wait on objects lock’s single condition accounts[from] -= amount; accounts[to] += amount; notifyAll(); // notify all threads waiting on the condition } public synchronized double getTotalBalance() {....} private double accounts[]; } Java Threads

  24. While using synchronized yields more concise code, implicit locks and conditions have some limitations. • Cannot interrupt a thread that is trying to acquire a lock • Cannot specify a timeout when trying to acquire a lock • Having a single condition per lock can be inefficient • The virtual machine locking primitives do not map well to the most efficient locking mechanisms available in hardware. Java Threads

  25. Core Java authors recommendations • Best to use neither Lock/Condition or the synchronized keyword. Should first see if java.util.concurrent package offers a solution. • If the synchronized keyword works for your solution, you should use it. You write less code and have less room for error. • Use Lock/Condition if you specifically need the additional power that these constructs give you. Java Threads

  26. Synchronized Blocks If you deal with legacy code (or legacy programmers :-), you need to know something about the built-in synchronization primitives. Recall each object has a lock. class Bank { public void transfer(int from, int to, double amount) { synchronized(lock) { // an ad-hoc lock accounts[from] -= amount; accounts[to] += amount; } } private double accounts[]; private Object lock = new Object(); } Java Threads

  27. Volatile Fields • Computers with multiple processors can temporarily hold memory values in registers or local memory caches. Threads running in different processors may see different values for the same memory location! • Compilers can reorder instructions for maximum throughput. Compilers won’t change the meaning of the code, but will assume that all changes occur within the block of optimized code. However, a memory value can be changed by another thread! Java Threads

  28. Concurrent access to a field is safe in these three conditions: • The field is volatile • The field is final, and it is accessed after the constructor has completed. • The field access is protected by a lock • NOTE: Prior to JDK 5.0, the semantics of volatile were rather permissive. Java Threads

  29. Deadlocks • Locks and conditions cannot solve all problems that might arise in multithreading. Consider ... • Account 1: $200 • Account 2: $300 • Thread 1: Transfer $300 from Account 1 to 2 • Thread 2: Transfer $400 from Account 2 to 1. • In the example, deadlocks can’t occur because each transfer is limited to $1000. With 100 acounts and $100,000 total, at least on account must have $1000. What if we change this restriction? Java Threads

  30. SynchBankTest_dl Java Threads

  31. Creating Deadlocks • You can create a deadlock by making the i’th thread responsible for putting money into the i’th account, rather than for taking it out of the i’th account. It becomes possible for all the threads to gang up on one account, each trying to remove more money that it contains. • Another deadlock situation: Change the signalAll method to signal in the SynchBankTest program. It deadlocks because it is now only unblocking a single thread, if that thread can’t proceed... • Unfortunately, there is nothing in the Java language to avoid or break these deadlocks. Java Threads

  32. Lock Testing and Timeouts • A thread blocks indefinitely when it calls the lock method to acquire a lock that is owned by another thread. Other things you can try: • tryLock boolean method. Returns true if lock acquired. • tryLock(100, TimeUnit.MILLISECONDS), with a timeout. tryLock can be unfair and grab the lock without waiting its turn, with the timeout tryLock is fair. • myCondition.await(1000, TimeUnit.MICROSECONDS), will return if notifyAll has been called by another thread, or if the timeout has elapsed. Java Threads

  33. Read/Write Locks • java.util.concurrent.locks package defines two lock classes, ReentrantLock and ReentrantReadWriteLock. The latter is advantageous if many threads read and only a few write. Java Threads

  34. private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private Lock readLock = rwl.readLock(); private Lock writeLock = rwl.writeLock(); public double getTotalBalance() { readLock.lock(); try { .... } finally { readLock.unlock(); } } public void transfer(...) { writeLock.lock(); try { ... } finally { writeLock.unlock(); } } Java Threads

  35. Why the stop and suspend Methods are Deprecated • JDK 1.0 defined a stop method that simply terminates a thread, and a suspend method that blocks a thread. • Both of these methods have been deprecated since JDK 1.2. The stop method is inherently unsafe, and experience has shown that the suspend method can lead to deadlocks. Java Threads

  36. stop method This method terminates all pending methods, including the run method. When a thread is stopped, it immediately gives up the locks on all objects that it has locked. This can leave objects in an inconsistent state. For example, suppose a TransferThread is stopped in the middle of moving money from one account to another, after the withdrawal and before the deposit? Now the Bank object is damaged. Java Threads

  37. suspend Method Unlike stop, suspend won’t damage objects. However, if you suspend a thread that owns a lock, then the lock is unavailable until the thread is resumed. If the thread that calls the suspend method tries to acquire the same lock, the program deadlocks. This situation occurs frequently in GUIs. If you want to suspend a thread, introduce a variable and test it in a safe place of your run method. Java Threads

More Related