Java Threads Multithreading 10-minute reference manual

Source: Internet
Author: User

1 synchronization

How to synchronize access to shared resources by multiple threads is one of the most basic problems in multithreaded programming. When multiple threads access shared data concurrently, the data is in an intermediate or inconsistent state, which affects the correct operation of the program. We often call this a race condition (race condition), which is called a critical area (critical section) for concurrent access to shared data. Synchronization is the order in which multiple threads enter critical areas to avoid the occurrence of competitive conditions.

1.1 Synchronized Keywords

Synchronized is the most commonly used keyword in Java multithreaded programming. All Java objects have their own unique implicit synchronization lock. The lock can only be obtained by one thread at a time, and other threads attempting to acquire the lock are blocked in the object's wait queue until the thread that acquired the lock releases the lock to continue working. Synchronized keywords are usually used in two ways. When the Synchronized keyword is used in a class method definition, all threads that call the method must obtain a lock on the current object. This is a simpler approach, but the granularity of synchronization is large, and when a thread executes a synchronous method of an object, it must have no other thread executing any of the synchronized methods of the object. In addition, all the code in the synchronization method is in the synchronization block, and the thread that obtains the lock must leave the method after all of the code has been executed to release the lock, which may involve only a subset of the access to the shared resource (such as a member variable) that needs to be synchronized, and the rest is not required. So coarse-grained synchronization obviously increases the wait time for other threads. Another usage of synchronized is that it works on an object and synchronizes only one piece of code rather than the entire method.

Synchronized (object) {

Code that needs to be synchronized

}

The object that synchronized here can be a member variable of a class, or it can be a class object (denoted by this). This usage allows programmers to synchronize different member variables as needed, rather than always the current class object, increasing flexibility.

It is worth mentioning that not only the object has a lock, the class itself also has its own lock, which makes the static method can also be modified with synchronized. The thread that accesses the synchronous static method needs to obtain a synchronization lock on the class to continue execution.

1.2 Volatile keyword

In the Java memory model, each thread has its own local storage (for example, a register) and allows the thread to have a copy of the value of the variable. This makes some atomic operations that do not need to be synchronized, such as the storage and reading of Boolean member variables, also become unsafe. Imagine that we have a Boolean member variable called done and a loop that stops when do is true, that loop is executed by a background thread, another UI thread waits for user input, and the user presses a button and then sets it to true to terminate the loop. Because the UI thread owns the done copy locally, the user simply sets his or her local done to true instead of updating the Do in the main memory when the button is pressed, so the background thread will not be terminated because it does not see the change. Even if done in main memory changes, the background thread will not be aware of the change in the done because its local variable values are not updated in a timely manner. One way to solve this problem is to provide synchronized setter and getter methods for done, because acquiring a synchronous lock forces all variables to write the value from the temporary store (register) to the main memory. In addition, Java provides a more elegant way to solve this problem: the volatile keyword. Each time a volatile variable is used, the JVM guarantees that its value is read from the main memory, and the JVM writes the value back to main memory each time the volatile variable is modified.

Volatile scenario is strict, it must be clearly seen that volatile only tells the JVM to read and write to the variable must be in main memory every time and prohibit the use of temporary copy to optimize, it is only for the JVM special memory model needs, and there is no synchronization function. So only the atomic operation (Reading and assignment) of the volatile variable is thread-safe, like self-increment + +, which still requires additional synchronization measures for operations that contain multiple commands.

Another thing to note is that when you modify an array with a volatile one, it simply says that the reference to the array is volatile, and that the elements in the array are the same as the normal variables, which may be optimized by the JVM, and we cannot add a volatile modifier to the elements in the arrays. The workaround for the above problem is to use the atomic variable. As an example of using a volatile modifier array, you can refer to java.util.concurrent.CopyOnWriteArrayList. Its add operation is done by copying the original array and adding the new element to the end of the new array and then pointing the internal array reference variable to the new array, so that the array variables are often modified and need to use volatile.

1.3 Explicit Lock Lock

Although the Synchronized keyword solves most synchronization problems, j2se5.0 also introduces the lock interface. We call lock as an explicit lock, compared to the use of the Synchronized keyword to obtain an implicit synchronization lock on an object. An obvious benefit of using an explicit lock is that it no longer belongs to an object, so it can be shared among multiple objects. The lock interface has lock () and Unlock () two methods, using them similar to the Synchronized keyword, calling lock before entering code that requires synchronization, and calling unlock when it leaves the synchronization code block. Usually the unlock is placed in finally to ensure that the lock can be released even if an exception occurs in the synchronized code block.

And the use of the Synchronized keyword and the lock () method always block different threads that fail to acquire locks, and the lock interface provides a non-blocking Trylock () method. A thread that calls the Trylock method returns false immediately if it fails to obtain a lock, and the thread can continue executing other code without waiting, which gives the programmer more freedom.

The lock interface also provides a newcondition () method that returns a condition object. The condition object acts the same as the wait-notify mechanism used for thread notifications.

1.4 Signal Volume semaphore

Sometimes we have multiple identical shared resources that can be used by multiple threads at the same time. We want to base the lock on a counter, based on the number of resources to initialize this counter, each successful lock operation will be the value of the counter minus 1, as long as the value of the counter is not zero to indicate that there are resources can be used, the lock operation can be successful. Each unlock operation will add 1 to this counter. The lock operation blocks the current thread only if the value of the counter is 0. This is the semaphore semaphore in Java.

The Semaphore class provides a method that is very similar to the lock interface, when the number of resources in the semaphore is set to 1 o'clock, the semaphore is degraded to a normal lock.

1.5 read/write lock Readwritelock

Access to shared resources can often be divided into read and write. While it may take a long time to read in some scenarios, we need to use mutexes to block concurrent writes to ensure data consistency. However, for concurrent read threads, there is no need to use synchronization. In fact, only the operations that make the data change need to be synchronized, we want to have a way to distinguish between read and write, the read and write operations are mutually exclusive, but multiple read operations can be done simultaneously, which can effectively improve the performance of read-intensive programs. J2SE5.0 provides the Readwritelock interface and provides the Reentrantreadwritelock class that implements the interface:

[Java]View Plaincopy
    1. Public interface Readwritelock {
    2. Lock Readlock ();
    3. Lock Writelock ();
    4. }

From the interface method it is not difficult to see that the read lock and write lock are included in the read and write lock. Implementing class Reentrantreadwritelock provides us with more convenient ways to use read-write locks, such as iswritelocked, which can be used to detect whether a write is locked.

2 Thread Notifications

In addition to the sync lock, Java object has two synchronization methods that can be used for inter-thread notifications, wait and notify. Wait and notify must be used in conjunction with synchronized, and a thread that obtains an object lock can temporarily discard the lock by calling the object's wait method, blocking itself in the object's waiting queue so that other threads waiting for the same lock will have an opportunity to execute. When the other thread that acquired the lock completes the work, it can invoke the Notify method to wake it up and continue execution. Each time the notify call wakes only one thread in the wait queue, the Notifyall method can wake all threads in that object waiting queue.

3 Minimizing synchronization

Thread synchronization solves the uncertainty caused by multiple threads competing for the same resource by allowing threads to enter the synchronization code block, but at the expense of efficiency, we need to use synchronization as little as possible in order to achieve better performance. In fact, not all competitive conditions need to be avoided, which can only cause problems if the race condition occurs in a non-thread-safe code snippet.

3.1 Atomic variable

If an operation is an atomic operation, such as assigning a Boolean variable, we do not need to synchronize. Java provides some atomic classes so that some are not atomic operations (such as the self-increment operation + +, which contains a value, plus 1, an assignment of three atomic operations) can also be atomic execution, which does not need to use synchronization.

Java provides 4 basic atomic classes, Atomicinteger, Atomiclong, Atomicboolean, and Atomicreference, respectively, providing atomic operations for Int,long,boolean,object. The interesting thing is that if you open the source code of the JDK and want to see how these atomic operations are implemented, you will be disappointed to find that there is no synchronization or other technology used in the codes. If you write the same code in your own program, then they are not atomic.

3.2 Thread Local variable

If each thread has its own private member variable, then we do not need to synchronize. Threadlocal is a private variable for a thread, and each thread that uses the threadlocal variable has its own independent threadlocal object, so there is no problem with multiple threads accessing the same variable. Of course, because the threadlocal variable is thread-private, it cannot be used to share state among multiple threads.

The Threadlocal class is not mysterious, its implementation is simple: Each thread object has its own container for storing private threadlocal objects Threadlocalmap, when a thread calls the Threadlocal object's Get () method to When a value is taken, the Get method first takes the current thread object, then pulls out the thread's threadlocalmap and checks if it is already in the map, and returns the value in the map directly if it already exists. If it does not exist, make yourself a key and initialize a value into the map of the current thread.

[Java]View Plaincopy
  1. Public T get () {
  2. Thread t = Thread.CurrentThread ();
  3. Threadlocalmap map = getmap (t);
  4. if (map! = null) {
  5. Threadlocalmap.entry e = map.getentry (this);
  6. if (E! = null)
  7. return (T) E.value;
  8. }
  9. return Setinitialvalue ();
  10. }

4 thread pool thread pool

Although the thread does not require as many resources as a process, its creation also has some overhead, and frequent creation and destruction of threads can degrade the performance of the program, and the number of threads that an application can create is subject to machine physical conditions, and too many threads deplete the machine's resources. So we need to limit the number of concurrent threads when designing a program. The common practice for solving both problems is to use the thread pool. The thread pool initializes several threads at the start of the thread (either on demand, or on a thread that is idle for a certain amount of time), and then the program takes the task to the thread pool to execute instead of handing it over to a thread, and the thread pool assigns threads to the tasks. When a thread finishes a task, it is set to idle for the next task to be reused instead of destroying it. The thread pool needs to specify the maximum number of threads at initialization, and when the number of concurrent tasks exceeds the number of threads, the thread pool will not create new threads but let new tasks wait, so that we do not have to worry about the number of threads running out of system resources. JDK1.5 started to provide us with a standard thread pool.

4.1 Actuator Executor

The Java thread pool implements the following executor interfaces:

[Java]View Plaincopy
    1. Public interface Executor {
    2. void Execute (Runnable command);
    3. }

In multithreaded programming, the executor is a common design pattern, its advantage is to provide a simple and efficient programming model, we just need to split the work of the concurrent processing into separate tasks, and then to the executor to execute can not care about thread creation, allocation and scheduling. The j2se5.0 provides two main functions: Threadpoolexecutor and Scheduledthreadpoolexecutor. Threadpoolexecutor is the basic thread pool implementation, scheduledthreadpoolexecutor on the basis of the former to increase the function of task scheduling, when the task is given to it we can specify the execution time of the task, rather than immediately execute.

Java.util.concurrent.Executors is the factory class used to create the thread pool, and with the factory method it provides, we can easily create thread pools of different attributes.

4.2 Future Interface

The executor interface does not look so ideal, and sometimes we perform a task to get the result of the calculation, sometimes we need to have more control over the task, such as knowing whether it is complete or terminating it halfway. The Execute method that returns void does not meet our requirements. Of course we can work on the incoming Runnable class to provide similar functionality, but it's tedious and error-prone. Since J2SE provides us with a standard implementation of thread pooling that frees us from multithreaded programming, these common requirements will certainly be well met. In fact, the thread pool implements a richer Executorservice interface that defines the Submit method that performs the task and returns the future object that represents the task.

Through the future interface, we can see whether the task that has been submitted to the thread pool is complete, gets the result of the execution, or terminates the task.

4.3 Runnable and callable interfaces

Classes that implement the runnable or callable interface can be submitted as tasks to the thread pool execution, The main difference between the two interfaces is that the callable call method has the result of returning and can throw an exception and the runnable Run method returns void and does not allow a check exception to be thrown (only the runtime exception is thrown). Therefore, if our task executes with the result returned, we should use the callable interface.

5 Thread and Collection Class 5.1 thread-safe collection classes
    • Java.util.Vector
    • Java.util.Stack
    • Java.util.HashTable
    • Java.util.concurrent.ConcurrentHashMap
    • Java.util.concurrent.CopyOnWriteArrayList
    • Java.util.concurrent.CopyOnWriteArraySet
    • Java.util.concurrent.ConcurrentLinkedQueue
5.2 Non-thread-safe collection classes
    • Java.util.BitSet
    • Java.util.HashSet (Linkedhashset)
    • Java.util.TreeSet
    • Java.util.HashMap (Weekhashmap, TreeMap, Linkedhashmap, Identityhashmap)
    • Java.util.ArrayList (LinkedList)
    • Java.util.PriorityQueue

These non-thread-safe collections can be wrapped into thread-safe collections by means of java.util.Collections.SynchronizedList, Synchronizedmap, Synchronizedset, and so on. The wrapper class simply adds synchronized protection to the various operations of the packaged set. It is important to note that additional synchronized protection must be added when using cursors to traverse these wrapper collections, otherwise there will be a problem.

[Java]View Plaincopy
    1. List List = Collections.synchronizedlist (new ArrayList ());
    2. ...
    3. Synchronized (list) {
    4. Iterator i = List.iterator (); //must is in synchronized block
    5. While (I.hasnext ())
    6. Foo (I.next ());
    7. }

5.3 Thread Notification Collection class
    • Java.util.concurrent.ArrayBlockingQueue
    • Java.util.concurrent.LinkedBlockingQueue
    • Java.util.concurrent.SynchronousQueue
    • Java.util.concurrent.PriorityBlockingQueue
    • Java.util.concurrent.DelayQueue

These collection classes implement the Blockingqueue interface. The blocking queue is characterized when an element is removed from the queue and if the queue is empty, the thread is blocked until an element in the queue is inserted. When an element is inserted from a queue, if the queue is full, the thread is blocked until an element in the queue has been fetched with free space. Blocking queues can be used to implement producer consumer patterns (Producer/consumer pattern).

Java Threads Multithreading 10-minute reference manual

Related Article

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.