|
In Part 1 of this series, we examined transactions and explored their basic properties -- atomicity, consistency, isolation, and durability. Transactions are the basic building blocks of enterprise applications; without them, it would be nearly impossible to build fault-tolerant enterprise applications. Fortunately, the Java Transaction Service (JTS) and the J2EE container do much of the work of managing transactions for you automatically, so you don't have to integrate transaction awareness directly into your component code. The result is almost a kind of magic -- by following a few simple rules, a J2EE application can automatically gain transactional semantics with little or no additional component code. This article aims to demystify some of this magic by showing how and where the transaction management occurs. If throwing InterruptedException means that a method is a blocking method, then calling a blocking method means that your method is a blocking method too, and you should have a strategy for dealing with InterruptedException. Often the easiest strategy is to throw InterruptedException yourself, as shown in the putTask() and getTask() methods in Listing 1. Doing so makes your method responsive to interruption as well and often requires nothing more than adding InterruptedException to your throws clause. While the Java language does not provide direct support for associative arrays -- arrays that can take any object as an index -- the presence of the hashCode() method in the root Object class clearly anticipates the ubiquitous use of HashMap (and its predecessor, Hashtable). Under ideal conditions, hash-based containers offer both efficient insertion and efficient retrieval; supporting hashing directly in the object model facilitates the development and use of hash-based containers. The CopyOnWriteArrayList class, in java.util.concurrent, can help prevent this problem. It implements List and is thread-safe, but its iterators will not throw ConcurrentModificationException and do not require any additional locking during traversal. This combination of features is achieved by reallocating and copying the list contents internally every time the list is modified, so that threads iterating the contents do not have to deal with changes -- from their perspective, the list contents remain constant during iteration. While this may sound inefficient, remember that in most Observer situations, each component has a small number of listeners, and insertions and removal are greatly outnumbered by traversals. So the faster iteration makes up for the slower mutation and provides better concurrency because multiple threads can iterate the list simultaneously. In the last installment of Java theory and practice , we examined the fork-join library, which will be added to the java.util.concurrent package in Java 7. Fork-join is a technique that makes it easy to express divide-and-conquer parallel algorithms in a way that admits efficient execution on a wide range of hardware, without code changes. Most Java texts properly describe the usage and consequences of using the final keyword, but offer little in the way of guidance as to when, and how often, to use final. In my experience, final is vastly overused for classes and methods (generally because developers mistakenly believe it will enhance performance), and underused where it will do the most good -- in declaring class instance variables. For most Java class libraries, the Javadoc is the only documentation. And, except for commercial software components, many Java classes have no Javadoc to speak of. While Javadoc is great as an API reference tool, it's a pretty poor way to learn how a class library is organized and how it should be used. And even when present, the Javadoc often contains only the most basic information about what a method does, ignoring important features such as error handling, domain and range for parameters and return values, thread safety, locking behavior, preconditions, postconditions, invariants, or side effects. Until JDK 5.0, it was not possible to write wait-free, lock-free algorithms in the Java language without using native code. With the addition of the atomic variables classes in the java.util.concurrent.atomic package, that has changed. The atomic variable classes all expose a compare-and-set primitive (similar to compare-and-swap), which is implemented using the fastest native construct available on the platform (compare-and-swap, load linked/store conditional, or, in the worst case, spin locks). Nine flavors of atomic variables are provided in the java.util.concurrent.atomic package (AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean; array forms of atomic integer; long; reference; and atomic marked reference and stamped reference classes, which atomically update a pair of values). For every bug pattern, there exists a corresponding proscriptive element of design advice exhorting us to avoid the specific bug pattern. So if FindBugs is a bug pattern detector, it stands to reason that it can also serve as an auditing tool to measure the degree of compliance with a set of design principles. Many installments of Java theory and practice are dedicated to specific elements of design advice (or its corresponding bug pattern). In this installment, I'll explain how FindBugs can help ensure that this design advice is followed in an existing codebase. Let's rehash some past advice and see how FindBugs can help detect when you fail to follow it. Nearly every Web application has some session state, which might be as simple as remembering whether you are logged in, or might be a more detailed history of your session, such as the contents of your shopping cart, cached results of previous queries, or the complete response history for a 20-page dynamic questionnaire. Because the HTTP protocol is itself stateless, session state needs to be stored somewhere and associated with your browsing session in a way that can be easily retrieved the next time you request a page from the same Web application. Fortunately, J2EE provides several means of managing session state -- state could be stored in the data tier, in the Web tier using the HttpSession interface from the Servlet API, in the Enterprise JavaBeans (EJB) tier using stateful session beans, or even in the client tier using cookies or hidden form fields. Unfortunately, injudicious management of session state can cause serious performance problems. An equivalent program in the Java language that tried to use the pseudo-typedef antipattern would run into trouble. The StringList and UserList types in Listing 3 both extend a common superclass, but they are not equivalent types. This means that any code that wants to call lookupAll must pass a StringList, not a List In a recent installment of the Crossing borders series, my friend and colleague Bruce Tate wrote about the power of closures using Ruby to illustrate. At the recent JavaPolis conference in Antwerp, one of the most well-attended sessions was Neal Gafter's "Adding closures to the Java language." And on the public white boards at JavaPolis, where attendees could write their thoughts about anything related to (or unrelated to) Java technology, nearly half the space was dedicated to the closures debate. It seems like everyone in the Java community is talking about closures these days -- even though closures were a well-developed concept more than 20 years before the development of the Java language. Methods that take objects as arguments should not mutate the state of those objects, unless they are explicitly documented to do so or are effectively assuming ownership of that object. When we pass an object to an ordinary method, we generally don't expect that the object will come back changed. However, with mutable objects, this is simply an act of faith. If we pass a java.awt.Point to a method such as Component.setLocation(), there's nothing stopping setLocation from modifying the location of the Point we pass in or from storing a reference to that point and changing it later in another method. (Of course, Component doesn't do this, because it would be rude, but not all classes are so polite.) Now, the state of our Point has changed without our knowledge, with potentially hazardous results -- we still think the point is in one place, when in fact it is in another. However, if Point were immutable, then such hostile code would not be able to modify our program state in such a confusing and dangerous way. Listing 3 illustrates a solution for SelectMaxProblem using the fork-join package that is scheduled for inclusion in Java 7. The package is being developed openly by the JSR 166 Expert Group, using the code name jsr166y, and you can download it separately and use it with Java 6 or later. (It will eventually live in the package java.util.concurrent.forkjoin.) The operation invoke-in-parallel is implemented by the coInvoke() method, which invokes multiple actions simultaneously and waits for them all to complete. A ForkJoinExecutor is like an Executor in that it is designed for running tasks, except that it specifically designed for computationally intensive tasks that do not ever block except to wait for another task being processed by the same ForkJoinExecutor. |
w___w__w.__j___a_va2___s__.__c___om | Contact Us |
Copyright 2009 - 12 Demo Source and Support. All rights reserved. |
All other trademarks are property of their respective owners. |