Java basics: multithreading Implementation/startup/status + synchronization + Thread Pool

Source: Internet
Author: User

 

 

From: http://zangweiren.blog.51cto.com/412366/94386

Original Author:Haina baichuan, an expert

 

 

Threads or multithreading are powerful tools for processing multiple tasks.

The threads and processes are different. Each process is an independent program with its own variables, and variables between different processes cannot be shared;

While the thread runs inside the process, each running process has at least one thread, and different threads can share data within the process range.

That is to say, a process has its own storage space, while a thread shares a storage space with other threads in the process to which it belongs.

 

The use of threads enables us to process tasks in parallel. The concurrent processing of threads brings a better user experience.

For example, when using the mail system (running processes such as outlook, Thunderbird, and Foxmail), you certainly do not want them to be unable to read even emails you have received when receiving new emails, however, you can only wait until the email receiving operation is completed.

This is exactly what the thread means.

 

[Thread implementation method]

 

There are two ways to implement the thread:

Inherit java. Lang. Thread, rewrite its run () method, and put the thread's execution body into it.

Implement the java. Lang. runnable interface, implement its run () method, and put the thread's execution body into it.

// This is an example of inheriting the Thread class implementation thread: <br/> public class threadtest extends thread {<br/> Public void run () {<br/> // write the thread execution subject here <br/> // do something <br/>}< br/> // This is the implementation example of how to implement multiple threads using the runnable interface: <br/> public class runnabletest implements runnable {<br/> Public void run () {<br/> // write the thread execution subject here <br/> // do something <br/>}< br/>} 

 

There is no big difference between the two implementation methods. It is easy to implement the method of inheriting the Thread class, but the class that inherits it cannot inherit other classes, so it cannot inherit useful methods of other classes. This problem does not exist when the runnable interface is used. In addition, this implementation separates the thread subject from the thread object, which is logically clear, we recommend that you use this method more.

 

 

[How to start a thread]

 

After we implement a thread in the above two ways, the thread instances are not created, so they are not run.

To start a thread, you must call a method to start it. This method is the start () method of the thread class, rather than the run () method.

 

(It is neither the run () method that inherits the override of the thread class nor the run () method that implements the runnable interface ).

The run () method contains the main body of the thread, that is, the code to be run after the thread is started. It has nothing to do with the startup of the thread.

 

The preceding two thread implementation methods are different at startup.

 

Inherit the Thread class startup method:

Public class threadstarttest {

Public static void main (string [] ARGs ){

// Create a thread instance

Threadtest TT = new threadtest ();

// Start the thread

TT. Start ();

}

}

 

How to enable the runnable interface:

Public class runnablestarttest {

Public static void main (string [] ARGs ){

// Create a thread instance

Thread t = new thread (New runnabletest ());

// Start the thread

T. Start ();

}

}

 

In fact, the two thread startup methods have the same principle. The first is to call a local method to start a thread, and the second is to execute the run () method of the target object in this thread. So what is the target object? To understand this problem, let's take a look at the implementation of the run () method of the thread class:

Public void run (){

If (target! = NULL ){

Target. Run ();

}

}

 

When the new thread (runnable target) constructor is called, set the instance of the class implementing the runnable interface to the target object of the subject to be executed by the thread. When the thread starts, the run () method of the instance is executed. When we use the thread inheritance method to implement the thread, the run () method of the thread is overwritten, so when the thread starts, the run () method of the object is executed. To sum up, the thread class has a target attribute of the runnable type, which is the subject of the run () method to be executed after the thread starts, if we use the method of inheriting the Thread class, the target is the thread object itself. If we use the method of implementing the runnable interface, the target is an instance of the class that implements the runnable interface.

 

 

[Thread status]

 

In Java 5.0 and later versions, the thread state is expanded to new, runable, blocked, waiting, timed waiting, and dead.

The thread state completely contains the entire lifecycle of a thread from creation to running, and from the end to the end.

 

The details of the thread status are as follows:

 

New (new status and initialization status ):

The thread object has been created but has not been started. This period of time is before the start () method is called after the new command is called.

 

Runnable (available and ready ):

The status of the thread after we call the START () method of the thread. A thread in the runnable state runs on the Java Virtual Machine (JVM), but it may still be waiting for the operating system to allocate corresponding running resources to it for running.

 

Blocked ):

The thread is waiting for other threads to release the synchronization lock to enter a synchronization block or the synchronization method to continue running; or it has already entered a synchronization block or synchronization method, during the running process, it calls an object inherited from Java. lang. the wait () method of the object, waiting to return the synchronous block or synchronous method again.

 

Waiting ):

The current thread calls Java. lang. object. wait (), Java. lang. thread. join () or Java. util. concurrent. locks. locksupport. one of the three methods in Park () is waiting for another thread to execute an operation. For example, if a thread calls the wait () method of an object, it is waiting for other threads to call notify () or policyall () of this object (these two methods are also inherited from the object class) method to wake it up; or a thread calls the join () (This method belongs to the Thread class) method of another thread and is waiting for the method to end.

 

Timed_waiting (timed wait state ):

The current thread calls Java. lang. object. wait (long timeout), Java. lang. thread. join (long millis), Java. util. concurrent. locks. locksupport. packnanos (long Nanos), Java. util. concurrent. locks. locksupport. any of the four methods of packuntil (long deadline) enters the waiting state, but unlike the waiting state, it has a maximum waiting time, even if the waiting condition is still not met, it will automatically wake up at this time.

 

Terminated ):

The status after the thread completes execution. After the thread executes all the code in the run () method, It exits from the method and enters the terminated state. Another case is that run () throws an exception during the running process, which is not caught by the program, causing the thread to terminate and enter the terminated state.

 

In Java and later versions, all six States of a thread are defined in the Java. Lang. Thread class in the form of enumeration. The Code is as follows:

Public Enum state {

New,

Runnable,

Blocked,

Waiting,

Timed_waiting,

Terminated;

}

 

Sleep () and wait ()

Both the sleep () method and the wait () method have the same effect of stopping the running thread. Let's take a closer look at their differences.

 

##### The sleep () method is a local method and belongs to the Thread class. It has two definitions:

Public static native void sleep (long millis) throws interruptedexception;

Public static void sleep (long millis, int Nanos) throws interruptedexception {

// Other code

}

The millis parameter represents the number of milliseconds (1‰ seconds), and nanos represents the number of nanoseconds (one thousandth of a second ).

Both methods can make the thread that calls it sleep (stop running) at the specified time. At this time, the thread will automatically wake up and change to runnable ), but this does not mean that it will be run immediately, because the thread scheduling mechanism also takes time to restore the thread.

Calling the sleep () method does not allow the thread to release the synchronization lock it holds, and during this period it does not impede the running of other threads.

 

##### The wait () method is also a local method, which belongs to the object class and has three definitions:

Public final void wait () throws interruptedexception {

// Do something

}

Public final native void wait (long timeout) throws interruptedexception;

Public final void wait (long timeout, int Nanos) throws interruptedexception {

// Do something

}

 

The wari () and wait (long timeout, int Nanos) methods are implemented based on the wait (long timeout) method. Similarly, timeout indicates the number of milliseconds, and nanos indicates the number of nanoseconds. When the wait () method of an object is called, the currently running thread will be transferred to the waiting state (waiting), waiting for other threads to call notify () or yyall () of this object again () method (these two methods are also local methods) to wake up, or the thread automatically wakes up at the specified maximum wait time. If a thread has a synchronization lock for one or more objects, after wait () is called, this thread will release all the synchronization resources it holds, this is not limited to the object that is called the wait () method. The wait () method is also interrupted by the interrupt () method of the thread class, and an interruptedexception exception is generated. The effect is the same as that of the sleep () method.

 

[Synchronous mode]

Synchronization is an important concept in multithreading. The use of synchronization ensures that the program will not produce any error results beyond the design in a multi-thread running environment. Same

Two methods are available: synchronous method and synchronous block. The synchronized keyword is used for both methods.

 

After adding the synchronized modifier to a method, you can make it a synchronous method. This method can be a static method or a non-static method, but not an abstract method of an abstract class, it cannot be an interface method in the interface. The following code is an example of a synchronization method:

Public synchronized void amethod (){

// Do something

}

Public static synchronized void anothermethod (){

// Do something

}

 

A thread is exclusive when executing a synchronous method. When any thread enters any synchronization method of an object, all the Synchronization Methods of this object are locked. During this period, no other thread can access any synchronization method of this object until the thread executes the synchronous method it calls and exits from it, as a result, it releases the synchronization lock of this object. After an object is locked by a thread, other threads can access all non-synchronous methods of the object.

 

Although the synchronous block format is different from the synchronous method, the principle and effect are consistent. The synchronization block synchronizes the Code contained in the synchronization block by locking a specified object. The synchronization method is to synchronize the code in this method block, in this case, the locked object is the subject object to which the synchronization method belongs.

What if this method is static synchronization? The thread is not the object of this class, nor the class itself, but the object of the Java. Lang. class type corresponding to this class. The synchronization method and the synchronization block are mutually restricted only between the same object. Therefore, the static synchronization method is restricted only by other static Synchronization Methods of its class, it has nothing to do with the instance (object) of this class.

 

The following code demonstrates how to implement synchronization blocks:

Public void test () {<br/> // synchronization lock <br/> string lock = "Lock "; <br/> // synchronization block <br/> synchronized (LOCK) {<br/> // do something <br/>}< br/> int I = 0; <br/> //... <br/>}< br/> 

There are no special requirements for objects used as synchronization locks. Any object can be used. If an object has both synchronous methods and synchronous blocks, the object will be locked when any of the synchronous methods or synchronization blocks is executed by a thread, other threads cannot access the synchronization method of this object at this time, nor can they execute synchronization blocks.

 

 

[Synchronized and lock]

The synchronization method is analyzed above: after adding the synchronized modifier to a method, it can be used as a synchronization method.

Lock is an interface that is located in the locks sub-package of the Java. utils. Concurrent package added in Java 5.0. The concurrent package and its sub-packages are used to process multi-threaded programming. The class implementing the lock interface has the same functionality as the synchronized keyword, but it is more powerful. Java. utils. Concurrent. locks. reentrantlock is a common class that implements the lock interface. The following is an application instance of the reentrantlock class:

Private lock = new reentrantlock (); <br/> Public void testlock () {<br/> // lock the object <br/> lock. lock (); <br/> try {<br/> // do something <br/>} finally {<br/> // release lock on the object <br/> lock. unlock (); <br/>}< br/>} 

 

The lock () method is used to lock objects. The unlock () method is used to release the lock on objects. They are all defined in the lock interface. When the code between the two methods is executed, the effect is equivalent to being placed in the synchronized synchronization block. The general usage is to put the code that needs to be executed between the lock () and unlock () methods in the try {} block, and call the unlock () method in the finally {} block, in this way, the object lock will always be released even if an exception is thrown during code execution. Otherwise, the deadlock may increase.

 

If you use the synchronized keyword to implement synchronization, all the Synchronization Methods and synchronization blocks of an object are considered as a whole. If one of them is called by a thread, others cannot be executed by other threads, even if there is no logical relationship between these methods or synchronization blocks and the called code, this obviously reduces the running efficiency of the program. The use of lock can solve this problem well. We can group the methods or code to be synchronized in an object according to the logical relationship, create a lock type object for each group, and implement synchronization. When a synchronization block is executed, this thread only locks the smallest set of other Code related to the current running code, and does not affect the invocation of other threads to other synchronous codes.

 

 

[About deadlocks]

A deadlock is a situation where every thread in a process is waiting for other threads in the process to release the resources occupied, so that all threads cannot continue to execute. Deadlock is a hidden trap in multi-threaded programming. It often occurs when multiple threads share resources. In actual development, deadlocks are generally hidden deep and are not easy to detect. Once deadlocks occur, they will inevitably lead to program paralysis. Therefore, it must be avoided.

 

The program must meet the following four conditions to cause a deadlock:

Mutual Exclusion: at least one of the resources used by a thread cannot be shared. It can only be used by one thread at a time.

Hold and wait: at least one thread already holds resources and is waiting to obtain the resources held by other threads.

No pre-emption: If a thread already holds a resource, other threads cannot snatch it for use before the thread releases the resource.

Loop wait (circular wait): Assume that n threads are running, the first thread holds a resource, and is waiting to obtain the resources held by the second thread, the second thread is waiting to obtain the resources held by the third thread ...... The Nth thread is waiting to obtain the resources held by the first thread, forming a loop wait.

 

 

[Thread pool]

A thread pool is an object pool just like a database connection pool. All object pools share a common purpose, that is, to improve the object usage and program efficiency. For example, for servlet, it is designed to be multi-threaded (if it is single-threaded, you can imagine that when 1000 people request a webpage at the same time, before the first person obtains the request results, other 999 people are waiting). If you create a new thread object for each request of each user to run, the system will spend a lot on creating and destroying threads, greatly reducing system efficiency. Therefore, a thread pool is supported behind the servlet multithreading mechanism. The thread pool creates a certain number of thread objects at the initial stage of initialization. By improving the utilization of these objects, avoid creating objects at high frequency to improve program efficiency.

 

The following is the simplest thread pool to understand its implementation principles. For this reason, we have defined four classes. Their usage and implementation are as follows:

Task: This is an abstract class that represents a task. It defines a deal () method, which must be implemented by inheriting the subclass of the task abstract class, and the specific work to be completed in the deal () method coding implementation. The thread in the thread pool is created to execute a variety of tasks and to facilitate the processing of tasks, we need to use the task abstract class to ensure that the specific work of the task is completed in the deal () method, which also makes the code more standardized.

Task is defined as follows:

Public abstract class task {<br/> Public Enum state {<br/>/* New */New,/* running */running, /* completed */finished <br/>}< br/> // task status <br/> private State state = state. new; <br/> Public void setstate (State state) {<br/> This. state = State; <br/>}< br/> public State getstate () {<br/> return state; <br/>}< br/> public abstract void deal (); <br/>} 

Taskqueue (task queue): At the same time, many tasks may need to be executed, while programs can only execute a certain number of tasks at the same time, what if the number of tasks to be executed exceeds the number of tasks that the program can afford? This gives you the rules for the tasks to be executed first and then those tasks to be executed. The taskqueue class defines one of these Rules. It adopts the FIFO (first in first out, and the English name is first in first out) method, that is, it is executed in the order of arrival of the task.

The taskqueue class is defined as follows:

Import Java. util. iterator; <br/> Import Java. util. using list; <br/> Import Java. util. list; <br/> public class taskqueue {<br/> private list <task> queue = new queue list <task> (); <br/> // Add a task <br/> Public synchronized void addtask (Task task) {<br/> If (task! = NULL) {<br/> queue. add (task ); <br/>}< br/> // Delete the task from the task queue after the task is completed <br/> Public synchronized void finishtask (Task task) {<br/> If (task! = NULL) {<br/> task. setstate (task. state. finished); <br/> queue. remove (task); <br/>}< br/> // obtain a task to be executed. <br/> Public synchronized task gettask () {<br/> iterator <task> it = queue. iterator (); <br/> task; <br/> while (it. hasnext () {<br/> task = it. next (); <br/> // search for a new task <br/> If (task. state. new. equals (task. getstate () {<br/> // set the task status to running <br/> task. setstate (task. state. running); <br/> return task; <br/>}< br/> return NULL; <br/>}< br/> 

The addtask method is used to add a new task to the task queue when it arrives. The Sequence List class is used to save the task arrival sequence. The finishtask (Task task) method is used to clear a task from the task queue when it is executed. The gettask () method is used to obtain the task to be executed.

Taskthread: It inherits from the Thread class and is used to execute tasks to be executed in the task queue.

Public class taskthread extends thread {<br/> // thread pool to which the thread belongs <br/> private threadpoolservice service; <br/> Public taskthread (threadpoolservice TPS) {<br/> service = TPS; <br/>}< br/> Public void run () {<br/> // execute the tasks in the task queue when the thread pool is running <br/> while (service. isrunning () {<br/> taskqueue queue = service. gettaskqueue (); <br/> task = queue. gettask (); <br/> If (task! = NULL) {<br/> task. deal (); <br/>}< br/> queue. finishtask (task); <br/>}< br/>} 

 

Threadpoolservice: this is the core class of the thread pool. It creates several thread objects when it is created, but these threads do not start and run, but they run only when the START () method is called to start the thread pool service. The stop () method can stop the thread pool service and stop the running of all threads in the pool. The runtask method is to submit a new task to be executed to the thread pool for running.

The threadpoolservice class is defined as follows:

Import Java. util. arraylist; <br/> Import Java. util. list; <br/> public class threadpoolservice {<br/> // Number of threads <br/> Public static final int thread_count = 5; <br/> // thread pool status <br/> private status = status. new; <br/> private taskqueue queue = new taskqueue (); <br/> Public Enum status {<br/>/* New */new, /* provide service */running,/* Stop Service */terminated, <br/>}< br/> private list <thread> threads = new arraylist <thread> (); <br/> Public threadpoolservice () {<br/> for (INT I = 0; I <thread_count; I ++) {<br/> thread t = new taskthread (this); <br/> threads. add (t); <br/>}< br/> // start the service <br/> Public void start () {<br/> This. status = status. running; <br/> for (INT I = 0; I <thread_count; I ++) {<br/> threads. get (I ). start (); <br/>}< br/> // stop the service <br/> Public void stop () {<br/> This. status = status. terminated; <br/>}< br/> // whether the instance is running <br/> Public Boolean isrunning () {<br/> return status = status. running; <br/>}< br/> // execute the task <br/> Public void runtask (Task task) {<br/> queue. addtask (task); <br/>}< br/> protected taskqueue gettaskqueue () {<br/> return queue; <br/>}< br/> 

After completing the above four classes, we have implemented a simple thread pool. Now we can use it. The following code provides a simple example:

Public class simpletasktest extends task {<br/> @ override <br/> Public void deal () {<br/> // do something <br/>}< br/> Public static void main (string [] ARGs) throws interruptedexception {<br/> threadpoolservice service = new threadpoolservice (); <br/> service. start (); <br/> // execute ten tasks <br/> for (INT I = 0; I <10; I ++) {<br/> service. runtask (New simpletasktest (); <br/>}< br/> // sleep for 1 second, waiting for all tasks to be completed <br/> thread. sleep (1000); <br/> service. stop (); <br/>}< br/>} 

 

Of course, what we implement is the simplest. Here we just want to demonstrate the implementation principle of the thread pool. In practical applications, many optimizations can be made based on different situations. For example:

Adjust the rules of the task queue and set the priority for the task. tasks with a higher level are executed first.

The thread pool is dynamically maintained. When the number of tasks to be executed is large, the number of threads is increased to speed up task execution. When there are few tasks, some Idle threads are reclaimed, reduce the consumption of system resources.

 

In fact, Java and later versions have provided us with the thread pool function, and there is no need to implement it again. These classes are located in the Java. util. Concurrent package.

 

The executors class provides a set of methods for creating thread pool objects. Common methods include:

Public static executorservice newcachedthreadpool () {<br/> // other Code <br/>}< br/> Public static executorservice newfixedthreadpool (INT nthreads) {<br/> // other Code <br/>}< br/> Public static executorservice newsinglethreadexecutor () {<br/> // other Code <br/>} 

Newcachedthreadpool () method to create a dynamic thread pool, where the number of threads is created and recycled based on actual needs, suitable for executing a large number of short-term tasks; newfixedthreadpool (INT nthreads) method To create a thread pool containing a fixed number of thread objects. nthreads indicates the number of threads to be created. If a thread is terminated due to an exception during running, A new thread is created and started to replace it. The newsinglethreadexecutor () method creates only one thread in the thread pool to execute all the tasks.

 

All three methods return an executorservice object. In fact, executorservice is an interface whose submit () method is used to receive tasks and submit them to the threads in the thread pool for running. The submit () method can accept callable and runnable objects. Their usage and differences are as follows:

Runnable interface: the class that inherits the runnable interface must implement its run () method and put the code for executing the task into it. The run () method has no return value. This method is suitable for only some operations without considering the running results.

Callable interface: the class that inherits the callable interface needs to implement its call () method and put the code for executing the task into it. Call () uses the execution result of the task as the return value. It is suitable for situations where the execution result needs to be known after an operation is executed.

 

Whether it is receiving runnable parameters or the submit () method that receives callable parameters, a future (also an interface) type object is returned. This object contains the task execution and results. You can call the Boolean isdone () method of future to check whether the task has been completed. Call the object get () method to obtain the returned results after the task is executed. If the task has not been completed yet, the get () method waits until the corresponding task is executed.

 

The following example is used to demonstrate the use of the thread pool in Java:

Import Java. util. concurrent. *; <br/> public class executortest {<br/> Public static void main (string [] ARGs) throws interruptedexception, <br/> executionexception {<br/> executorservice es = executors. newsinglethreadexecutor (); <br/> future Fr = es. submit (New runnabletest (); // submit the task <br/> future fc = es. submit (New callabletest (); // submit the task <br/> // obtain the returned value and output it <br/> system. out. println (string) FC. get (); <br/> // check whether the task has been completed <br/> If (Fr. isdone () {<br/> system. out. println ("execution completed-runnabletest. run () "); <br/>}else {<br/> system. out. println ("unexecuted-runnabletest. run () "); <br/>}< br/> // check whether the task has been completed <br/> If (FC. isdone () {<br/> system. out. println ("execution completed-callabletest. run () "); <br/>}else {<br/> system. out. println ("not executed-callabletest. run () "); <br/>}< br/> // stop the thread pool service <br/> es. shutdown (); <br/>}< br/> class runnabletest implements runnable {<br/> Public void run () {<br/> system. out. println ("Run-runnabletest. run () "); <br/>}< br/> class callabletest implements callable {<br/> Public object call () {<br/> system. out. println ("-callabletest. call () "); <br/> return" Return Value-callabletest. call () "; <br/>}< br/> running result: <br/>-runnabletest has been executed. run () <br/>-callabletest has been executed. call () <br/> return value-callabletest. call () <br/> execution completed-runnabletest. run () <br/> execution completed-callabletest. run () 

After the thread pool is used, you need to call its shutdown () method to stop the service. Otherwise, all the threads will remain running and the program will not exit.

 

 

 

This article is from the blog "author Haina baichuan". For more information, contact the author!

 

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.