----- Begin Included Message ----- From nobody@hermes.java.sun.com Tue Mar 28 15:16 EST 2000 Date: Tue, 28 Mar 2000 18:12:12 GMT X-Authentication-Warning: hermes.java.sun.com: Processed from queue /bulkmail/data/ed_6/mqueue2 X-Mailing: 195 From: JDCTechTips@sun.com Subject: JDC Tech Tips March 28, 2000 To: JDCMember@sun.com Mime-Version: 1.0 J D C T E C H T I P S TIPS, TECHNIQUES, AND SAMPLE CODE WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, March 28, 2000. This issue focuses on threads. The tip has three parts: * Why Use Threads? * Protecting Shared Resources with Synchronized Blocks * Minimizing the Overhead of Synchronized Blocks This tip was developed using Java(tm) 2 SDK, Standard Edition, v 1.2.2. You can view this issue of the Tech Tips on the Web at http://developer.java.sun.com/developer/TechTips/2000/tt0328.html. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WHY USE THREADS? Support for concurrent programming is a central feature of the Java (tm) programming language. The java.lang package includes the Thread class, which allows you to start multiple threads of execution in your program. On a multi-cpu machine, threads increase performance by allowing your code to use all of the available processors. Even on a uniprocessor box, there are compelling reasons to use threads. Tasks should be placed on separate threads if they take a long time to finish, or if they force the processor to wait for disk or network access. Dividing tasks into these "worker" threads has two advantages. First, the code runs faster because the cpu does not waste time idly waiting for resources. Second, the code is more responsive, because the user interface can have its own thread which handles user input even while the "worker" threads are busy. Of course, this power comes with a price. If more than one thread can access a piece of data, then you must guarantee that simultaneous access does not corrupt that data. The second part of this tip addresses this problem, showing you how to use synchronized blocks to protect data shared between threads. The third part of the tip presents some ideas for minimizing the overhead of synchronized blocks in your code. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PROTECTING SHARED RESOURCES WITH SYNCHRONIZED BLOCKS The example code for this tip manages mutual funds. When the program begins, all of the money in the fund is equally divided among several accounts. The fund is run by a group of fund managers, who may at any time decide to shift money from one account to another. The fund managers act independently of one another; to represent that fact, each fund manager has a thread dedicated to it. For simplicity, and to make it easy to detect concurrency problems when they occur, the total account value never changes--money simply moves from one account to another. Here's the code: /** * The FundConstants interface is a container for constants * that are shared by all classes. Any class that needs * unqualified access to these names implements the interface */ interface FundConstants { static final int noOfStocks = 3; static final int noOfManagers = 150; static final int testDuration = 20; static final int randomTransfers = 1000; static final int initialBalance = 10000; static final int totalMoneyInUniverse = noOfStocks * initialBalance; } /** * Data class representing a desired stock transfer */ class Transfer implements FundConstants { final public int fundFrom; final public int fundTo; final public int amount; public Transfer() { fundFrom = (int)(Math.random() * noOfStocks); fundTo = (int)(Math.random() * noOfStocks); amount = (int)(Math.random() * 1000); } } /** * Contains the current value of each stock, plus methods to * transfer between stocks and verify that the total value is * correct. */ class Stocks implements FundConstants { static int[] balances = new int[noOfStocks]; static { for (int n=0; n 3 aload_0 4 getfield #15 7 dup2 8 iaload 9 aload_0 10 getfield #12 13 isub 14 iastore It is not important that you understand exactly what the bytecodes do. The important thing to understand is that your one line of code translates into multiple lines of bytecode. In particular, the loading and storing of the entry in the balances array happens in two separate steps (numbered 8 and 14 above). On most Java(tm) implementations, a thread may be context-switched at any time. This means that the current state of the thread is saved, and another thread is allowed a chance to execute. What happens if a thread gets context-switched between steps 8 and 14, and another thread comes along and changes the value of the stock's balance? That is, what if the following happens: Thread 1 loads balance 2000 Thread 1 switched out, thread 2 allowed to run Thread 2 loads balance 2000 Thread 2 adds 500 Thread 2 stores new balance 2500 Thread 2 eventually switches out, thread 1 allowed to run Thread 1 subtracts 100 (from 2000, not 2500!) Thread 1 stores 1900 The change made by Thread 2 is permanently lost, and money has been seemingly destroyed. Investors are not happy. You might not see the problem behavior on your machines, which suggests that the problem occurs with low probability. However, though it might first appear counterintutive, bugs that manifest rarely are worse than those that occur frequently. By this standard, data corruption in a threaded program is a bug of the nastiest sort: it is hard to spot the problem by reading the code, it occurs only rarely in the field, and it may disappear entirely when you run the code in a debugger! Use a synchronized block to avoid basic data corruption problems in threaded Java programs. The synchronized block looks like this: synchronized (someObject) { //code to be protected } Each object in the Java language is associated with its own concurrency protection object, called a monitor. When a thread encounters a synchronized block, it must acquire the monitor associated with someObject before proceeding into the block. When the thread exits a synchronized block, it releases the monitor. Only one thread at a time may acquire a monitor, which guarantees that there will never be more than one thread at a time inside the block. If several blocks of code synchronize on the same object, then only one thread can be in the entire set of blocks. So, to protect data, you need to (1) identify which pieces of data might be touched by multiple threads, and (2) protect that data in a synchronized block. In the Stocks example, the endangered data is the balances array. The balances array is accessed from both the transfer and checkSystem methods, so both methods will need synchronized blocks: static void transfer(Transfer t) { synchronized (balances) { balances[t.fundFrom] -= t.amount; balances[t.fundTo] += t.amount; } } static void checkSystem() { int actual = 0; synchronized (balances) { for (int n=0; n