Java multithreading and Multithreading

Source: Internet
Author: User
Tags finally block

Java multithreading and Multithreading
1. Differences between a thread and a process 1.1 thread and a process

  • A process is an independent space in the memory and can be used to run the current application. The current process schedules all running details of the current program (the operating system allocates an independent running space for the process );
  • A thread is located in a process and is responsible for a space in the current process that is qualified to run independently (the process allocates a separate space for the thread). A process is responsible for the execution of a program, A thread is responsible for running an independent function in a process. A process must contain at least one thread.
  • Multiple Threads can be enabled in a process to allow multiple threads to complete a task at the same time. Multithreading is used to improve program execution efficiency.
1.2 thread running status

 

After a Thread object is created through the Thread class or Runnable interface, it enters the initial state; call the start method to enter the ready state (ready state), which is not the real operation, it only indicates that all the equipment before running is ready. If the thread obtains the cpu time slice, it enters the real runable state and executes the business logic in the run method; if the run method is completed or the stop method is called, the thread stops running and enters the dead state. Calling different methods in the running state also enters different states, if you call the force running method join or sleep method, the system enters the waiting state. After the time arrives, the system automatically enters the ready state. You are ready to obtain the cpu time slice at any time. If you see synchronized, the system enters the waiting state of the synchronization queue, or, if the wait method is called, the thread enters the waiting state. The thread in the waiting state must be awakened by running y to enter the waiting state, when the thread obtains the synchronization lock, it enters the ready state and waits for the cpu time slice to be obtained. Whether a thread is executed depends on whether it can compete for cpu time slice. However, by increasing the priority, the thread is preferentially executed with a higher probability.

2. Multithreading

The principle of Multithreading is: Switching time slices in the cpu thread. The cpu is responsible for program execution. At each point in time, it can only run one program instead of multiple programs. It constantly switches between multiple programs at high speed, A program is actually a process, namely multiple threads. In the end, the cpu continuously performs high-speed switching between multiple threads, and enabling multiple threads means not letting the cpu stop, squeeze it to the maximum extent to serve the program. There are three methods to implement multithreading: Inherit the Thread class, implement the Runnable interface, and use the Thread pool.

2.1 inherit the Thread class
Public class MyExtendsThread extends Thread {String flag; public MyExtendsThread (String flag) {this. flag = flag;} @ Override public void run () {String name = Thread. currentThread (). getName (); System. out. println ("Thread" + name + "started working... "); Random random = new Random (); for (int I = 0; I <20; I ++) {try {Thread. sleep (random. nextInt (10) * 100); System. out. println (name + "==============" + flag);} catch (InterruptedException e) {e. printStackTrace () ;}} public static void main (String [] args) {Thread t0 = new MyExtendsThread ("t0"); Thread t1 = new MyExtendsThread ("t1 "); t0.start (); t1.start (); // t0.run (); // t1.run ();}}

The call thread must use the start method instead of the run method. The run method is only used to call the method, but the Main thread is actually executed. The thread competition can be clearly seen when the start method is called.

2.2 implement the Runnable interface
Public class MyThreadImplementRunnable implements Runnable {int x; public MyThreadImplementRunnable (int x) {this. x = x ;}@ Override public void run () {String name = Thread. currentThread (). getName (); System. out. println ("Thread" + name + "start execution"); Random random = new Random (); for (int I = 0; I <20; I ++) {try {Thread. sleep (random. nextInt (10) * 100); System. out. println (name + "==============" + x);} catch (InterruptedException e) {e. printStackTrace () ;}} public static void main (String [] args) {Thread t1 = new Thread (new MyThreadImplementRunnable (1), "Thread 1 "); thread t2 = new Thread (new MyThreadImplementRunnable (2), "Thread 2"); t1.start (); t2.start ();}}
2.3 Callable interface implementation
  • Create the class MyThreadImplementCallable that implements the Callable interface;
  • Create a Class Object: MyThreadImplementCallable callable = new MyThreadImplementCallable ("test ");
  • Create a FutureTask object by Callable: FutureTask futureTask = new FutureTask (callable); Note: FutureTask is a package, which is created by accepting Callable, and implements both the Future and Runnable interfaces.
  • A Thread object is created by FutureTask: Thread thread = new Thread (futureTask );
  • Start thread: thread. start ();
  • Obtain the task thread execution result futureTask. get (); note: the thread implementing the Callable interface can obtain the execution result of the task thread; the thread implementing the Runnable interface cannot obtain the task thread execution result.
Public class MyThreadImplementCallable implements Callable <String> {String name; public MyThreadImplementCallable (String name) {this. name = name ;}@ Override public String call () throws Exception {Thread thread = Thread. currentThread (); System. out. println (thread. getName () + "start work =================="); Random random = new Random (); Thread. sleep (random. nextInt (5) * 100); // return name + ": Execution completed";} public static void main (String [] args) throws Exception {MyThreadImplementCallable callable = new MyThreadImplementCallable ("test"); FutureTask <String> futureTask = new FutureTask <String> (callable); Thread thread = new Thread (futureTask); thread. start (); String result = futureTask. get (); // obtain the task thread execution result System. out. println ("thread execution result:" + result );}}
2.4 Use thread pool

See the thread pool lecture below. References: Comparison and usage of Callable and Runnable and four methods for creating threads

3. Synchronize the 3.1synchronized keyword
Public class MySynchronized {public static void main (String [] args) {final MySynchronized synchronized = new MySynchronized (); new Thread ("thread1 ") {@ Override public void run () {synchronized (synchronized1) {try {System. out. println (this. getName () + ": start"); Thread. sleep (1, 1000); System. out. println (this. getName () + ": wake up"); System. out. println (this. getName () + ": end");} catch (InterruptedException e) {e. printStackTrace ();}}}}. start (); new Thread ("thread2") {@ Override public void run () {synchronized (synchronized1) {// before Thread 1 is released, thread 2 can only wait // synchronized (synchronized2) {// if it is not a lock, you can see two sentences of cross-printing, a race against the System. out. println (this. getName () + ": start"); System. out. println (this. getName () + ": end ");}}}. start ();}}

Synchronized is a keyword in java and is a built-in feature of java. If a code block is modified using synchronized, the code is synchronized. When a thread obtains the lock and starts to execute the lock, other threads can only wait until the thread executes and then release the lock, there are only two reasons for releasing the lock: 1. the thread is successfully executed. 2. an exception occurs during thread execution. jvm Automatically releases the lock. We can see that after the synchronized keyword is used, there will only be one shared code in the thread Execution code block at a time, thread security. The disadvantage is also obvious. Other threads can only wait for the lock to release, causing serious resource waste.

3.2Lock Interface
  • The difference between lock and synchronized: Lock is not built-in Java. It is an interface through which synchronous access can be implemented. synchronized is a keyword of Java and a built-in feature; lock is a little different from synchronized. When synchronized is used, you do not need to manually release the Lock. After the synchronized method or synchronized code block is executed, the system automatically releases the thread to occupy the Lock; the Lock requires the user to manually release the Lock. If the Lock is not released, the deadlock may occur. Lock is an interface with the following methods:
public interface Lock {    void lock();    void lockInterruptibly() throws InterruptedException;    boolean tryLock();    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;    void unlock();}

Lock (), tryLock (), tryLock (long time, TimeUnit unit), and lockInterruptibly () are used to obtain the lock. The unLock () method is used to release the lock.

  • The lock is used to obtain the Lock. As mentioned above, if the lock is used, the Lock must be released actively. Even if an exception occurs, the program will not automatically release the Lock. Therefore, in general, the Lock must be used in the try {} catch {} block and the Lock release operation should be carried out in the finally block, to ensure that the lock is released to prevent deadlock.
Public class MyLock {private static ArrayList <Integer> arrayList = new ArrayList <Integer> (); private static Lock lock = new ReentrantLock (); public static <E> void main (String [] args) {new Thread () {@ Override public void run () {Thread thread = Thread. currentThread (); lock. lock (); // get the lock try {System. out. println (thread. getName () + "get lock"); for (int I = 0; I <5; I ++) {arrayList. add (I) ;}} catch (Exception e) {} finally {System. out. println (thread. getName () + "released lock"); lock. unlock (); // release the lock }};}. start (); new Thread () {@ Override public void run () {Thread thread = Thread. currentThread (); lock. lock (); try {System. out. println (thread. getName () + "get lock"); for (int I = 0; I <5; I ++) {arrayList. add (I) ;}} catch (Exception e) {} finally {System. out. println (thread. getName () + "released lock"); lock. unlock ();}};}. start ();}}
  • TryLock () indicates to try to get the lock. If the acquisition is successful, true is returned. If the acquisition fails (that is, the lock has been obtained by other threads), false is returned, that is to say, this method will return immediately in any case and will not wait until the lock is available.
  • The tryLock (long time, TimeUnit unit) method is similar to the tryLock () method. The difference is that this method will wait for a certain period of time when the lock is not available. If the lock cannot be obtained within the time limit, returns false. Returns true if the lock is obtained from the beginning or within the waiting period.
// Observed phenomenon: After a thread acquires the lock, the other thread cannot obtain the lock and will not wait until the public class MyTryLock {private static List <Integer> arrayList = new ArrayList <Integer> (); private static Lock lock = new ReentrantLock (); public static void main (String [] args) {new Thread ("Thread 1") {@ Override public void run () {Thread thread = Thread. currentThread (); boolean tryLock = lock. tryLock (); System. out. println (thread. getName () + "=======" + tryLock); if (tryLock) {try {System. out. println (thread. getName () + "get lock"); for (int I = 0; I <20; I ++) {arrayList. add (I);} Thread. sleep (1000);} catch (Exception e) {e. printStackTrace ();} finally {lock. unlock (); System. out. println (thread. getName () + "lock released ");}}}}. start (); new Thread ("Thread 2") {@ Override public void run () {thread Thread = Thread. currentThread (); boolean tryLock = lock. tryLock (); System. out. println (thread. getName () + "=======" + tryLock); if (tryLock) {try {System. out. println (thread. getName () + "get lock"); for (int I = 0; I <20; I ++) {arrayList. add (I) ;}} catch (Exception e) {e. printStackTrace ();} finally {lock. unlock (); System. out. println (thread. getName () + "lock released ");}}}}. start ();}}

Thread 1 and thread 2 Share the member variable arrayList. When thread 1 acquires the lock, thread 2 cannot obtain the lock and cannot execute the business logic of thread 1, after the lock is released, thread 2 can obtain the lock and execute its code to ensure thread security.

  • The lockInterruptibly () method is special. When you use this method to obtain the lock, if the thread is waiting to obtain the lock, the thread can respond to the interruption, that is, the waiting state of the thread to be interrupted. Note that when a thread acquires the lock, it will not be interrupted by the interrupt () method. Therefore, when the lockInterruptibly () method is used to obtain a lock, if the lock cannot be obtained, it can be interrupted only when the lock is waiting. With synchronized, when a thread is waiting for a lock, it cannot be interrupted.
  • Implementation class of the Lock interface -- If ReentrantLock directly uses the lock interface, we need to implement many methods, which is inconvenient. ReentrantLock is the only class that implements the Lock interface, and ReentrantLock provides more methods, reentrantLock, which means "reentrant Lock". You can use it to create a Lock object.
  • ReadWriteLock is also an interface in which only two methods are defined:
public interface ReadWriteLock {    Lock readLock();    Lock writeLock();}

One is used to obtain the read lock and the other is used to obtain the write lock. That is to say, the file read/write operations are separated and divided into two locks to be allocated to the thread, so that multiple threads can perform read operations simultaneously.

  • ReentrantReadWriteLock provides many methods, but there are two main methods: readLock () and writeLock () to obtain the read lock and write lock. The result of using this read/write Lock operation is: either all the read operations are performed, and all the write operations are performed after the end, and no cross-read-write is performed in the middle.
/*** @ Author Liu Jun-heavy * If one thread has occupied the read lock, other threads will wait until the read lock is released if they want to apply for the write lock. * If one thread occupies the write lock, if other threads apply for the write lock or read lock, the applied thread will remain waiting to release the write lock. */Public class callback {ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock (); public static void main (String [] args) {final MyReentrantReadWriteLock myTest = new callback (); new Thread ("Thread 1") {@ Override public void run () {myTest. read (Thread. currentThread (); myTest. writer (Thread. currentThread ());}}. start (); new Thread ("Thread 2") {@ Override public void run () {myTest. read (Thread. currentThread (); myTest. writer (Thread. currentThread ());}}. start ();}/*** @ Description read Method * @ Author Liu Jun weight * @ Date */private void read (Thread thread) {readWriteLock. readLock (). lock (); try {long start = System. currentTimeMillis (); while (System. currentTimeMillis ()-start <= 1) {System. out. println (thread. getName () + "=== reading operation in progress") ;}} catch (Exception e) {e. printStackTrace ();} finally {readWriteLock. readLock (). unlock (); System. out. println (thread. getName () + "= release the read lock") ;}/ *** @ Description write Method * @ Author Liu Jun heavy * @ Date */private void writer (Thread thread) {readWriteLock. writeLock (). lock (); try {long start = System. currentTimeMillis (); while (System. currentTimeMillis ()-start <= 1) {System. out. println (thread. getName () + "=== executing write operation") ;}} catch (Exception e) {e. printStackTrace ();} finally {readWriteLock. writeLock (). unlock (); System. out. println (thread. getName () + "= release the write lock ");}}}

Select Lock and Synchronized:

  • Lock is an interface, and sysnchrinized is a java keyword, which belongs to the built-in language implementation;
  • The synchronized keyword will release the lock after the program runs successfully or when an exception occurs. Using the lock will not automatically release the lock. You can only use the unlock to release the lock yourself. Otherwise, it will cause a deadlock. It is best to release it in finally;
  • You can use the trylock method to determine whether or not to obtain the lock. synchronized cannot be used to determine whether or not to obtain the lock;
  • The use of lock can interrupt the thread waiting for the lock. The use of synchronized cannot interrupt the thread and can only wait forever;
  • The use of lock can improve the efficiency of multi-threaded read operations. Conclusion: if the competition for resources is not intense, synchronized and lock will be used at the same time, and if there are a large number of threads competing at the same time, the lock will be far better than synchronized.
4. volatile keywords

The main memory is used during program execution, and each thread also has its own working memory. When a thread starts to work, it will copy a copy of the variable from the primary memory to the working memory, and then update it back to the primary memory after the copy is completed in the working memory. When multiple threads exist, if the processing time of working memory A is not enough to update back to the primary memory, work memory B pulls this variable from the primary memory, obviously, this variable is not the latest data and may cause problems. How can this problem be solved? Volatile can be used. One of the most significant features of volatile is its visibility into the modified variables. What does it mean when a thread modifies the value of a variable, the new value is immediately synchronized to the main memory. The latest variable value is obtained when other threads use it. Although volatile can ensure variable visibility, it cannot guarantee thread security because it cannot guarantee atomicity. To ensure thread security, you must use synchronization or lock. There is a document written volatile well, paste it: http://dwz.cn/76TMGW

5. Thread Pool

After JDK1.5, advanced concurrency is introduced. util. the concurrent package is specially used for multi-thread concurrent programming. It makes full use of modern computer multi-processor and multi-core functions to compile large-scale concurrent applications. It mainly includes atomic weights, concurrent sets, synchronizers, and reentrant locks, and provides strong support for thread pool creation.

5.1 five ways to create a thread pool
  • Single Thread Executor: A Thread pool with only one Thread. All submitted tasks are executed sequentially. Code: Executors. newSingleThreadExecutor ()
  • Cached Thread Pool: multiple threads in the Thread Pool need to be executed at the same time. The old available threads will be triggered and re-executed by new tasks. If the Thread is not executed within 60 seconds, it will be terminated and deleted from the pool; Code: Executors. newCachedThreadPool ()
  • Fixed Thread Pool: A Thread Pool with a Fixed number of threads. If no task is executed, the Thread will remain waiting. Code: Executors. the parameter 4 in the constructor of newFixedThreadPool (4) is the size of the thread pool. You can set it as needed. It is best to set it to be consistent with the number of cpu cores, obtain the number of cpu cores int cpuNums = Runtime. getRuntime (). availableProcessors ();
  • Scheduled Thread Pool: the Thread Pool used to schedule the tasks to be executed. It may not be executed directly. It is a strategy type to execute every few times. Code: Executors. newScheduledThreadPool ()
  • Single Thread Scheduled Pool: there is only one Thread used to schedule tasks to be executed at a specified time. The Code: Executors. newSingleThreadScheduledExecutor () Example code is as follows:
Public static void main (String [] args) {ExecutorService newSingleThreadExecutor = Executors. newSingleThreadExecutor (); ExecutorService newCachedThreadPool = Executors. newCachedThreadPool (); // obtain the number of cpu cores int num = Runtime. getRuntime (). availableProcessors (); ExecutorService newFixedThreadPool = Executors. newFixedThreadPool (num); ScheduledExecutorService newScheduledThreadPool = Executors. newScheduledThreadPool (8); ScheduledExecutorService newSingleThreadScheduledExecutor = Executors. newSingleThreadScheduledExecutor ();}
5.2 use of Thread Pool

Before talking about the use of the thread pool, I would like to emphasize the "Callable" twin brother of Runnable, which is very similar, but the Runnable run method does not have any returned results, and the main thread cannot obtain the returned values of the task thread; however, the call method of Callable can return results, but the main thread is blocked when obtaining the results. You need to wait for the task thread to return the results. Therefore, Callable is more powerful than Runnable, so how can we get this execution result? The answer is Future. You can use Future to obtain the Callable execution result. There are two ways to use the thread pool: Runnable and Callable:

  • Submit Runnable. After the task is completed, the Future object returns null, calls excute, submits the task, and anonymous Runable overrides the run method. The run method contains the business logic. Sample Code:
Public class TestPoolWithRunnable {public static void main (String [] args) throws Exception {ExecutorService pool = Executors. newFixedThreadPool (4); for (int I = 0; I <10; I ++) {Future <?> Submit = pool. submit (new Runnable () {@ Override public void run () {System. out. println (Thread. currentThread (). getName () + "start execution") ;}}); System. out. println ("execution result:" + submit. get (); // All execution results are null} pool. shutdown (); // closes the thread pool }}
  • Submit Callable. This method returns a Future instance indicating the status of the task. It calls submit to submit the task, anonymous Callable, and override the call method. If a return value exists, obtaining the return value will be blocked, wait until the thread task returns the result.
/*** @ Author * differences between Callable and Runnable: * The Runnable run method does not return any results, therefore, the main thread cannot obtain the return value of the task thread * The call method of Callable can return the result, but the main thread is blocked when obtaining the result, you must wait for the task thread to return the result */public class TestPoolWithCallable {public static void main (String [] args) throws Exception {ExecutorService pool = Executors. newFixedThreadPool (4); for (int I = 0; I <10; I ++) {Future <String> future = pool. submit (new Callable <String> () {@ Override public String call () throws Exception {Thread. sleep (500); return "=" + Thread. currentThread (). getName () ;}}); // get results from Future. This method will be blocked until the thread task returns the result System. out. println ("execution result:" + future. get ();} pool. shutdown ();}}

How can I solve the problem of blocking execution results? When using the future. get () method to obtain results, this method is blocked. How can we improve the efficiency? If you do not need to obtain the execution result immediately, you can put the execution result in the queue first. After the program is executed, the execution result of each thread is obtained. The sample code is as follows:

Public class TestThreadPool {public static void main (String [] args) throws Exception {Future <?> Submit = null; // create the cache thread pool ExecutorService cachePool = Executors. newCachedThreadPool (); // List of Callable execution results <Future <?> FutureList = new ArrayList <Future <?> (); For (int I = 0; I <10; I ++) {// cachePool submit thread, Callable, Runnable no return value // submit = cachePool. submit (new TaskCallable (I); submit = cachePool. submit (new TaskRunnable (I); // put these execution results in the list, and then obtain them later to avoid blocking futureList. add (submit);} cachePool. shutdown (); // print the execution result for (Future f: futureList) {boolean done = f. isDone (); System. out. println (done? "Completed": "Not completed"); System. out. println ("thread return result:" + f. get ());}}}

Place the submit in the list set, and obtain it after the thread completes the straight line.

6. java concurrent programming summary 6.1 disadvantages of not using the thread pool

The new Thread (). start () method is used directly, which is normal in general scenarios. However, if concurrent requests are high, the following risks may occur:

  • Overhead of the new thread. Although a thread is much lighter than a process, the cost of creating a thread is huge for the JVM. It is never the same as creating an object.
  • Resource consumption. Without a pool to limit the number of threads, the number of threads will directly depend on the number of concurrent applications.
  • Stability. When the number of threads exceeds the limits of system resources, stability becomes a problem.
6.2 thread pool type

Whether you create a thread pool through Executors or manage it through Spring, you must know which thread pools are available:

  • FixedThreadPool: Specifies the thread pool. A thread is created when a task is submitted until the maximum capacity of the pool. If a thread ends unexpectedly, a new thread is added;
  • CachedThreadPool: A Variable Thread Pool. It is like a spring. If there is no job requirement, it recycles Idle threads. If the demand increases, it increases the number of threads as needed, without limiting the pool size;
  • SingleThreadExecutor: single thread. Tasks that cannot be processed will enter the FIFO queue for execution;
  • SecheduledThreadPool: periodic thread pool. In fact, different types of thread pools are implemented by constructing a ThreadPoolExecutor. The difference is corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory parameters.
6.3 thread pool saturation Policy

From the above thread pool types, we can see that other thread pools except CachedThreadPool may be saturated. When they are saturated, corresponding policies are required to process the tasks of the Request thread. For example, ThreadPoolExecutor is used to reach the upper limit. the setRejectedExecutionHandler method sets a denial policy. JDK provides several policies: AbortPolicy, CallerRunsPolicy, DiscardPolicy, and DiscardOldestPolicy.

 

I have a public account that will often share some Java-related things. If you like it, you can search for "Java headers" or "javatuanzhang.

Refer:
  • Java multi-thread programming Summary
  • How to create and run a java thread

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.