"Effective Java"--Learning Notes (Exception & concurrency) __java

Source: Internet
Author: User
Tags modifier static class volatile concurrentmodificationexception
Exception 57th: Use exceptions only for exceptions

Exceptions should be used only in the case of exceptions: they should never be used for normal control flow

A well-designed API should not force its clients to use exception 58th for normal flow of control: Use an exception for a recoverable condition, use a Run-time exception for programming errors

The Java programming language provides three types of throwing structures: tested exceptions, run-time exceptions, and errors

If the caller is expected to recover appropriately, the exception should be used in this case, forcing the caller to handle the exception in a catch clause, or to propagate it, by throwing the client exception. Therefore, declaring each tested exception to be thrown in a method is a potential indication to the API user that the condition associated with the exception is a possible result of invoking the method

If the program throws an unhandled exception or error, it is often an unrecoverable situation, and it's not good to continue.

Errors are often reserved for use by the JVM to indicate a lack of resources, a constraint failure, or other conditions that make a program unable to continue execution, since this is already an almost universally accepted practice, so it is best not to implement any new error subclasses, so All outstanding throw structures should be subclasses of the runtimeexception (direct or indirect) 59th: Avoid unnecessary use of the inspected exception

Excessive use of the tested exception will make the API very inconvenient to use, if the proper use of the API does not prevent this exception conditions, and once the exception, the use of API programmers can take useful action immediately, then the test should be tested exception should be used 60th: Priority to use standard exceptions

The Java Platform Class Library provides a basic set of unhandled exceptions that meet the exception-throwing needs of most APIs, and there are many benefits to reusing existing exceptions, such as easier learning and use, better readability, etc.

Common exceptions: IllegalArgumentException when the caller passes a parameter value that is not appropriate, it often throws the exception illegalstateexception if the call is illegal because the state of the object is accepted, this exception is usually thrown Null indexoutofboundsexception subscript bounds passed in the NullPointerException parameter concurrentmodificationexception If an object is designed to be dedicated to a single thread or used in conjunction with an external synchronization mechanism, it should throw the exception if it is (or has) been modified concurrently unsupportedoperationexception if the object does not support the requested operation, this exception is thrown 61st: Throw the exception corresponding to the abstract

Higher-level implementations should capture low-level exceptions while throwing exceptions that can be interpreted by high-level abstractions, known as exception translation

Public E get (int index) {
    listiterator<e> i = listiterator (index);
    try {return
        i.next ();
    } catch (Nosuchelementexception e) {
        throw new Indexoutofboundsexception ("Index:" + index);
    }

A special form of exception translation is called an anomaly chain, and if the underlying exception is useful for debugging problems that cause high level exceptions, it is appropriate to use the exception chain

try{.
} catch (Lowerlevelexception cause) {
    throw new higherlevelexception (cause);
}

Class Higherlevelexception extends exception{
    higherlevelexception (throwable cause) {
        super (cause);
    }
}

If you cannot block or handle exceptions from a lower level, the general practice is to use exception translation, unless the low-level method happens to ensure that all the exceptions it throws are appropriate for the upper levels to propagate the anomaly from the lower level to the top. The exception chain provides the best functionality for both high-level and low-level exceptions: it allows you to throw appropriate high-level exceptions while capturing the underlying causes for failure analysis 62nd: Each method throws an exception with a document

Always declare the tested exception individually, and use the @throws tag of Javadoc to accurately record the conditions that throw each exception, but do not use the throws keyword to include the unhandled exception in the declaration of the method 63rd: Include information in the detail message that captures the failure

In order to catch a failure, the details of the exception should contain all the parameters that contribute to the exception and the value of the field. For example, the details of the indexoutofboundsexception exception should contain the lower bound, upper bounds, and subscript values that do not fall within bounds.

To ensure that the details of the exception contain enough information to capture the failure, one approach is to introduce the information in the constructor of the exception instead of the string detail message, and then, with this information, you can automatically generate a detail message by placing them in the message description. For example, Indexoutofboundsexception can have a constructor like this:

Public indexoutofboundsexception (int lowerbound, int upperbound, int index) {
    super ("Lower bound:" + lowerbound +
  
    ", Upper bound:" + Upperbound + 
          ", index:" + index);

    Save failure information for programmatic access
    this.lowerbound = lowerbound;
    This.upperbound = Upperbound;
    This.index = index;
}

  
64th: Efforts to keep the failure of atomic

A failed method call should keep the object in the state before it was called, and a method with this property is called a failed atomicity

For mutable objects, you can check the validity of a parameter before you perform an action

Public Object pop () {
    if (size = = 0)
        throw new Emptystackexception ();

    Object result = Elements[--size];
    Elements[size] = null;
    return result;
}

A similar way to obtain failure atomicity is to adjust the order of the calculation process so that any calculated part that may fail occurs before the object state is modified

Another way to get Atomicity is to perform an operation on a temporary copy of an object, and then replace the contents of the object with the result of the temporary copy when the operation is complete. 65th: Do not ignore exceptions

When the designer of the API declares that a method will throw an exception, it should not ignore (an empty catch block)

try{...
} catch (Someexception e) {

}
Concurrent 66th: Synchronizing access to shared variable data

Synchronization is necessary for reliable communication between threads and for mutually exclusive access.

If access to the shared variable data is not synchronized, the consequences will be dire, even if the variable is the basic type of an atom that can be read and written (a long and double type is not an atom). To prevent one thread from interfering with another, it is recommended that the first thread poll a Boolean field, which starts with false, but can be set to true through the second thread to indicate that the first thread will terminate itself. Because both the read and write operations of a Boolean domain are atomic, the program no longer uses synchronization when it accesses the domain:

public class Stopthread {

    private static Boolean stoprequested;

    public static void Main (string[] args) throws interruptedexception {
        thread backgroundthread = new Thread (new Runnable () {
            @Override public
            void Run () {
                int i = 0;
                while (!stoprequested) {i++}}}
        );

        Backgroundthread.start ();

        TimeUnit.SECONDS.sleep (1);
        Stoprequested = true;
    }
}

Because the above code is not synchronized, the virtual opportunity will have this code:

while (!done)
    i++;

Turn into this:

if (!done) while
    (true)
        i++;

This JVM optimization can lead to activity failure: This program cannot advance. One way to fix this problem is to synchronize access to the Stoprequest domain as follows:

public class Stopthread {

    private static Boolean stoprequested;

    private static synchronized void RequestStop () {
        stoprequested = true;
    }

    private static Synchronized Boolean stoprequested () {return
        stoprequested;
    }

    public static void Main (string[] args) throws interruptedexception {
        thread backgroundthread = new Thread (new Runnable () {
            @Override public
            void Run () {
                int i = 0;
                while (!stoprequested ()) {i++}}}
        );

        Backgroundthread.start ();

        TimeUnit.SECONDS.sleep (1);
        RequestStop ();
    }

The actions of the synchronized methods in the Stopthread are atomic even if they are not synchronized, and these methods are synchronized only for the purpose of their communication, not for mutually exclusive access

You can also use the volatile modifier to not perform mutually exclusive access, but it guarantees that any thread will see the most recently written value when it reads the domain:

public class Stopthread {

    private static volatile Boolean stoprequested;

    public static void Main (string[] args) throws interruptedexception {
        thread backgroundthread = new Thread (new Runnable () {
            @Override public
            void Run () {
                int i = 0;
                while (!stoprequested) {i++}}}
        );

        Backgroundthread.start ();

        TimeUnit.SECONDS.sleep (1);
        Stoprequested = true;
    }
}

Be careful when using volatile, consider the following method, assuming it will produce a serial number:

private static volatile int nextserialnumber = 0;

public static int Generateserialnumber () {return
    nextserialnumber++;
}

Because the increment operator (+ +) is not atomic, it performs two operations in the Nextserialnumber domain: first, it reads the value and then writes back a new value. If the second thread reads this field while the first thread reads the old value and writes back a new value, the second thread sees the same value along with the first thread and returns the same serial number. This is a security failure: This program calculates the wrong result.

The amendments are as follows:

private static final Atomiclong Nextserialnum = new Atomiclong ();

public static long Generateserialnumber () {return
    nextserialnum.getandincrement ();
}

Having a thread modify a data object in a short time and then share it with other threads is acceptable, synchronizing only the actions of the shared object reference, and then the other thread can read the object without further synchronization, only it has not been modified. This object is called de facto immutable, and the passing of such an object reference from one thread to another is called a security release. There are many ways to safely publish object references: You can save it in a static domain as part of class initialization, you can save it in a volatile domain, a final domain, or a domain that is accessed through a normal lock, or you can put it in a concurrent collection

In short, when multiple threads share mutable data, each thread that reads or writes data must perform a synchronization. The volatile modifier is an acceptable form of synchronization if you need only interactive communication between threads, but not mutexes, but it may take some skill to use it correctly 67th: avoid excessive synchronization

Excessive synchronization can result in performance degradation, deadlock, and even indeterminate behavior. Within a synchronized area, do not invoke a method designed to be overwritten, or a method provided by the client in the form of a function object, such that the method is foreign, does not know what the method will do, and cannot control it, as in the following example:

public class Observableset<e> extends forwardingset<e> {public observableset (set<e> Set) {super (SE T);

    Private final list<setobserver<e>> observers = new arraylist<setobserver<e>> (); public void Addobserver (setobserver<e> observer) {synchronized (observers) {Observers.add (o
        bserver);
            } public boolean Removeobserver (setobserver<e> observer) {synchronized (observers) {
        return Observers.remove (Observer); }//This is the culprit private void notifyelementadded (E element) {synchronized (observers
        {for (setobserver<e> observer:observers) observer.added (this, element);
        @Override public boolean Add (E Element) {Boolean added = Super.add (element);
        if (added) notifyelementadded (element);
    return added; } @Override PubliC Boolean AddAll (collection<? extends E> C) {Boolean result = false;  for (E element:c) result |= Add (element);
    Calls notifyelementadded return result;
 }
}

Observer cancel a subscription by calling the Removeobserver method by calling the Addobserver method subscription notification

public static void Main (string[] args) {
    observableset<integer> set =
        new Observableset<integer> ( New hashset<integer> ());

    Set.addobserver (New setobserver<integer> () {public
        void added (observableset<integer> s, Integer e) { C4/>system.out.println (e);
            if (e = d) s.removeobserver (this);
        }
    );

    for (int i = 0; i < i++)
        Set.add (i);
}

After printing out the 0~23 number, the above program does not stop, but throws concurrentmodificationexception. The problem is that when notifyelementadded calls the Observer's added method, it is in the process of traversing the observers list. The added method invokes the Removeobserver method of the collection to invoke Observers.remove, attempting to remove an element from the list during the traversal of the list, which is illegal

You can resolve this problem by removing a synchronized code block from the call to a foreign method

private void notifyelementadded (E element) {
    list<setobserver<e>> snapshot = null;
    Synchronized (observers) {
        snapshot = new arraylist<> (observers);
    }
    for (setobserver<e> observer:snapshot) {
        observer.added (this, element);
    }
}

In general, you should do as little work as possible in the synchronization area, get the lock, check the shared data, transform the data as needed, and then drop the lock. If you have to perform a time-consuming operation, you should try to move the operation outside of the synchronization area

Never be overly synchronized, in this multi-core era, the actual cost of excessive synchronization is not the amount of CPU time spent acquiring a lock, but the loss of parallel opportunities, as well as the need to ensure that each kernel has a consistent memory view of the delay, and another potential cost of excessive synchronization is It limits the ability of the VM to optimize code execution

If a variable class is to be used concurrently, the class should be made thread-safe, with internal synchronization, which can be significantly more concurrent than locking the entire object externally, otherwise, do not synchronize internally

If you synchronize classes internally, you can use different methods to achieve high concurrency, such as split locks, detach locks, and non-blocking concurrency control

If the method modifies the static domain, it must also synchronize access to the domain, even though it is often used only for a single thread 68th: Executor and tasks take precedence over threads

The java.util.concurrent.Executor of the Java platform is a flexible, interface-based task execution tool

If you're writing a small program, or a lightweight server, using Executors.newcachedthreadpool is usually a good choice because it doesn't need to be configured and generally works correctly. However, the cached thread pool is not a good choice for a heavily loaded server, in a cached thread pool, the committed task is not queued, but is delivered directly to the thread, and if no thread is available, a new thread is created, and if the server is overloaded so that all of its CPUs are securely occupied, When there are more tasks, more threads will be created, which will only make the situation worse. Therefore, it is best to use Executors.newfixedthreadpool in a heavily loaded product server, which provides a thread pool containing the number of fixed threads, or uses the Threadpoolexecutor class directly for maximum control 69th: Concurrency tools prior to wait and notify

It's more difficult to use wait and notify correctly, and you should use more advanced concurrency tools instead

Concurrent collections provide high-performance concurrency implementations for standard collection interfaces such as lists, queue, and map, which are internally managed synchronously to provide high concurrency. This means that the client cannot make a method call to the concurrent collection atomically, so some of the collection interfaces have been extended through dependent modification operations, merging several basic operations into a single atomic operation. For example, Concurrentmap extends the map interface and adds several methods, including putifabsent (key, value), inserts a map for the key when there is no mapping, returns the previous value associated with the key, and returns null if there is no such value.

In addition to delivering superior concurrency, Concurrenthashmap is very fast and can greatly enhance the performance of concurrent applications

Some collection interfaces have been extended through blocking operations, and they are waiting (or blocking) until they can be executed successfully. For example, Blockingqueue extends the queue interface and adds several methods, including take, that remove and return the header element from the queue, and wait if the queue is empty. This allows blocking queues to be used for work queues, also known as producer-consumer queues, one or more producer threads that add work items to a work queue, and when work items are available, one or more consumer threads take out queues from the work queue and process work items. Most executorservice implementations use Blockingqueue 70th: documentation for thread security

A class in order to be safely used by multiple threads, it must be clearly stated in the document that the level of thread security it supports is immutable-instances of this class are invariant, so there is no need for external synchronization, such examples include string, long, and BigInteger unconditional thread safety- Instances of this class are mutable, but this class has enough internal synchronization, so its instances can be used concurrently without any external synchronization, including random and concurrenthashmap conditional thread safety-- In addition to some methods that require external synchronization for secure concurrent use, the same thread security level is the same as unconditional thread security, such examples include the collection returned by the Collections.synchronized wrapper, whose iterators require external synchronization as not thread-safe-- This class instance is mutable, and in order to use them concurrently, the customer must surround each method call (or call sequence) with an external synchronization of their choice, such examples include a common set implementation, such as ArrayList and HashMap threads--a class that cannot be safely used concurrently by multiple threads , even though all method calls are surrounded by external synchronizations, the root cause of thread antagonisms is that static data is not modified synchronously, because of the consequences of not considering concurrency

Each class should describe or thread-safe annotations that clearly describe its thread-safe properties in the document, the synchronized modifier has nothing to do with this document, and a conditional thread-safe class must indicate in the document "which method call sequence requires an external synchronization. And what locks (usually, the lock that acts on the instance itself) are to be obtained when executing these sequences. If you are writing an unconditional thread-safe class, you should consider using a private lock object instead of a synchronized method, which prevents the client program and subclasses from interfering with each other, and the flexibility to adopt a more complex approach to concurrency control in subsequent releases. 71st: Cautious delay initialization

Lazy initialization is the behavior that is deferred until the value of the domain is required. This method applies both to the static domain and to the instance domain, although lazy initialization is primarily an optimization, but it can also be used to break the harmful loops in class and instance initialization

Deferred initialization reduces the overhead of initializing a class or creating an instance, but increases the cost of accessing a domain that has been deferred initialized. If the domain is accessed only in the instance part of the class, and the cost of initializing the domain is high, it may be worthwhile to delay initialization

Lazy initialization is tricky when there are multiple threads, and it is important to use some form of synchronization if two or more threads share a domain that is delayed initialization. In most cases, normal initialization takes precedence over deferred initialization

If you need to use deferred initialization for static domains for performance reasons, use the lazy initialization holder class mode

private static class fieldholder{
    static final FieldType field = Computefieldvalue ();
}

Static FieldType GetField () {return
    Fieldholder.field;
}

When the GetField method is first invoked, it reads the Fieldholder.field for the first time, causing the Fieldholder class to be initialized

If you need to use lazy initialization for the instance domain for performance reasons, use double check mode, which avoids locking overhead when the domain is initialized to access the domain

private volatile fieldtype field;
FieldType GetField () {
    FieldType result = field;
    if (result = = NULL) {//A/A (no locking)
        synchronized (this) {result
            = field;
            if (result = = NULL) {//Second check (with locking)
                field = result = Computefieldvalue ()
            ;
        }
    }} return result;
}

If you need to delay initializing an instance field that can accept duplicate initialization, you can use the single check mode

private volatile fieldtype field;

Private FieldType GetField () {
    FieldType result = field;
    if (result = = null) {
        field = result = Computefieldvalue ();
    }
    return result;
}
72nd article: Do not rely on the thread scheduler

The best way to write robust, responsive, portable multithreaded applications is to ensure that the average number of running threads is not significantly higher than the number of processors. This makes the thread scheduler have no more choice: it only needs to run these running threads until they are no longer available to run. Even under fundamentally different thread scheduling algorithms, the behavior of these programs will not change much. 73rd: Avoid using thread groups

Thread groups do not provide much useful functionality, and many of the features they provide are flawed, and if a class is designed to handle logical groups of threads, it may be possible to use a thread pool executor

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.