Program | multithreading | Design One: Understanding multithreading
Multithreading is a mechanism that allows multiple instruction streams to be executed concurrently in a program, each of which is called a thread, independent of each other. A thread, also known as a lightweight process, has independent execution control as a process, and the operating system is responsible for scheduling, the difference being that the thread does not have separate storage space, but rather shares a single storage space with other threads in the process, making communication between threads much simpler than the process.
The execution of multiple threads is concurrent, logically "at the same time", regardless of whether it is physically "simultaneous". If the system has only one CPU, then true "at the same time" is not possible, but because the CPU speed is very fast, users do not feel the difference, so we do not care about it, we just need to imagine that each thread is executed at the same time.
Multithreading and the traditional single thread in the design of the biggest difference is that because the control flow of each thread is independent of each other, so that the code between the threads are executed, the resulting thread scheduling, synchronization and other issues, will be discussed later.
Two: Implement multithreading in Java
We might as well imagine what we need to do to create a new thread. Obviously, we have to specify the code that this thread is going to execute, and that's all we need to do in Java.
It's amazing! How does Java do this? Through the Class! As a fully object-oriented language, Java provides a class java.lang.Thread to facilitate multithreaded programming, a class that provides a number of ways to control our own threads, and our future discussions will revolve around this class.
So how do we provide Java with the code we want the thread to execute? Let's take a look at the Thread class. The most important method of the thread class is run (), which is invoked by the method start () of the thread class, providing the code to be executed by our thread. In order to specify our own code, only need to overwrite it!
Method One: Inherit the thread class, overwrite method run (), we rewrite run () in the subclass of the thread class that we created, and we add the code to execute. Here is an example:
public class Mythread extends Thread {
int count= 1, number;
public mythread (int num) {
Number = num;
SYSTEM.OUT.PRINTLN ("Create thread" + number);
}
public void Run () {
while (true) {
SYSTEM.OUT.PRINTLN ("thread" + number + ": Count" + count);
if (++count== 6) return;
}
}
public static void Main (String args[]) {
for (int i = 0; i〈5 i++) New Mythread (i+1). Start ();
}
}
This method is simple and clear, in line with everyone's habit, however, it also has a great disadvantage, that is, if our class has been inherited from a class (such as applets must inherit from the Applet Class), then the Thread class can no longer inherit, if we do not want to create a new class, what should we do?
We may as well explore a new approach: we do not create subclasses of the thread class, but use it directly, so we can only pass our method as a parameter to an instance of the thread class, somewhat like a callback function. But Java has no pointers, we can only pass an instance of the class that contains the method. So how do you limit this class to include this method? Of course, use the interface! (Although abstract classes can be satisfied, but need to be inherited, do we want to adopt this new approach to avoid the limitations of inheritance?) )
Java provides an interface java.lang.Runnable to support this approach.
Method Two: Implement Runnable interface
The runnable interface has only one method run (), and we complete this part by declaring our class implements the Runnable interface and providing this method to write our thread code into it. However, the Runnable interface does not have any support for threading, and we must also create an instance of the thread class, which is implemented by the constructor of the thread class, public thread (Runnable target). Here is an example:
public class Mythread implements Runnable {
int count= 1, number;
public mythread (int num) {
Number = num;
SYSTEM.OUT.PRINTLN ("Create thread" + number);
}
public void Run () {
while (true) {
SYSTEM.OUT.PRINTLN ("thread" + number + ": Count" + count);
if (++count== 6) return;
}
}
public static void Main (String args[]) {
for (int i = 0; i〈5 i++) New Thread (New Mythread (i+1)). Start ();
}
}
Strictly speaking, it is also possible to create an instance of the thread subclass, but it must be noted that the subclass must not overwrite the thread class's Run method, otherwise the thread would execute the subclass's Run method rather than the run method of the class we used to implement the Runnable interface. A nuisance test.
Using the Runnable interface to implement multithreading allows us to include all of the code in a class, which is good for encapsulation, and its disadvantage is that we can only use a single set of code, and if you want to create multiple threads and have different code for each thread, you still have to create additional classes, if so, In most cases, it might not be as easy to inherit Thread directly from multiple classes.
To sum up, two methods are different, we can flexibly use.
Now let's look at some of the problems with multithreading.
Three: Four states of the thread
1. New state: The thread has been created but has not yet been executed (start () has not yet been invoked).
2. Executable state: The thread can execute, although it is not necessarily executing. CPU time may be assigned to the thread at any time, allowing it to execute.
3. State of death: normally run () return causes the thread to die. Calling Stop () or destroy () has the same effect, but is not recommended, the former will produce an exception, the latter is forced to terminate, will not release the lock.
4. Blocking status: The thread will not be allocated CPU time and cannot be executed.
Four: The priority of the thread
The priority of the thread represents how important the thread is, when more than one thread is in an executable state and waits for CPU time, the thread scheduling system determines to whom CPU time is allocated according to the priority of each thread, the higher priority thread has a greater chance of getting CPU time, and the lower priority thread is not without opportunity , just a little less chance.
You can invoke the method GetPriority () and SetPriority () of the thread class to access the priority of the threads, with the thread priority bounded between 1 (min_priority) and ten (max_priority), and the default is 5 (norm_ PRIORITY).
Five: Synchronization of Threads
Because multiple threads of the same process share the same piece of storage space, bringing convenience, but also brings the access conflict this serious problem. The Java language provides a specialized mechanism to resolve this conflict, effectively avoiding the same data object being accessed concurrently by multiple threads.
Since we can guarantee that data objects can only be accessed by methods through the private keyword, we simply propose a mechanism for the method, which is the Synchronized keyword, which includes two usages: Synchronized method and synchronized block.
1. Synchronized method: Declare the Synchronized method by adding the Synchronized keyword to the method declaration. Such as:
Public synchronized void Accessval (int newval);
The Synchronized method Controls access to class member variables: Each class instance corresponds to a lock, each synchronized method must obtain a lock that invokes the class instance of the method to execute, otherwise the owning thread is blocked, and once the method is executed, the lock is exclusive until the lock is released when the method returns , the blocked thread can then get the lock and re-enter the executable state. This mechanism ensures that at the same time for each class instance, at most only one of the member functions declared as synchronized is in the executable state (since at most one can obtain the lock of that class instance), This effectively avoids the access violation of class member variables (as long as all possible methods of accessing class member variables are declared as synchronized).
In Java, not just the class instance, each class also corresponds to a lock, so we can also declare the static member function of the class as synchronized to control its access to the static member variables of the class.
The flaw of the Synchronized method: If a large method is declared as synchronized, it will greatly affect efficiency, typically, if the thread class method run () is declared as synchronized, because it is running throughout its lifetime, Therefore, the invocation of any synchronized method in this class will never succeed. Of course, we can do this by putting the code that accesses the class member variable into a specialized method, declaring it as synchronized, and tuning it in the main method to solve the problem, but Java provides us with a better solution, which is the synchronized block.
2. Synchronized BLOCK: Declare synchronized block by synchronized keyword. The syntax is as follows:
Synchronized (SyncObject) {
Code that allows access control
}
A synchronized block is a block of code in which the code must obtain the lock of the object SyncObject (as described previously, can be a class instance or Class), and the specific mechanism is described in the previous instance. Because you can target arbitrary blocks of code, and can arbitrarily specify locked objects, so flexibility is high.
Six: Thread blocking
In order to solve the access conflict to the shared storage area, Java introduced the synchronization mechanism, now let us examine the multiple threads access to shared resources, obviously synchronization mechanism is not enough, because at any time the requested resources are not necessarily ready to be accessed, in turn, There may be more than one resource ready at the same time. In order to solve the problem of access control in this case, Java introduces the support of blocking mechanism.
Blocking refers to suspending the execution of a thread to wait for a certain condition to occur (such as a resource is ready), and the students who have learned the operating system must already be familiar with it. Java provides a number of ways to support blocking, let's analyze each of these.
1. Sleep () method () () allows you to specify a period of time in milliseconds as a parameter, which causes the thread to enter a blocking state within a specified amount of time, unable to get CPU time, and a thread to re-enter the executable state at the specified time.
Typically, sleep () is used when waiting for a resource to be ready: After the test finds that the condition is not satisfied, let the thread block for a period of time and then test again until the condition is satisfied.
2. Suspend () and resume () methods: Two methods are used, suspend () causes the thread to enter a blocking state, and does not automatically recover, the corresponding resume () must be called to enable the thread to re-enter the executable state. Typically, suspend () and resume () are used to wait for a result from another thread: After the test finds that the result has not been generated, the thread blocks, and the other thread produces the result, invoking resume () to recover it.
3. Yield () Method: Yield () causes the thread to discard the currently divided CPU time, but does not block the thread, that is, the thread is still in an executable state and may be able to get CPU time again at any time. The effect of calling yield () is equivalent to the scheduler's belief that the thread has performed enough time to go to another thread.
4. Wait () and Notify () method: Two methods supporting the use of, wait () to make the thread into a blocking state, it has two forms, one is allowed to specify in milliseconds for a period of time as a parameter, the other has no parameters, the former when the corresponding notify () The thread will re-enter the executable state when invoked or when the specified time is exceeded, and the latter must be called by the corresponding notify ().
At first glance they are no different from the suspend () and resume () methods, but in fact they are quite different. The core of the distinction is that all of the methods described previously do not release the occupied locks (if they are taken), and the opposite is true for each method.
The core differences mentioned above lead to a series of differences in detail.
First, all of the methods described earlier are subordinate to the Thread class, but the pair is directly subordinate to the object class, which means that all objects have this pair of methods. At first glance this is incredible, but in fact it is natural, because this pair of methods is blocked to release the occupied lock, and the lock is any object has, the call of any object's wait () method causes the thread to block, and the lock on the object is freed. The Notify () method that invokes any object causes a randomly selected unblocking in a thread that is blocked by invoking the wait () method of the object (but not really executable until the lock is acquired).
Secondly, all the methods described above can be invoked at any location, but this pair of methods must be invoked in the Synchronized method or block, and the reason is simple, the lock can be released only if the current line Cheng the lock in the Synchronized method or block. In the same way, the lock on the object that calls the method must be owned by the current thread so that the lock can be freed. Therefore, this pair of method calls must be placed in such a synchronized method or block that the lock object of the method or block is the object that invokes the pair of methods. If this condition is not met, the program can still compile, but the illegalmonitorstateexception exception appears at run time.
The above characteristics of the wait () and Notify () methods determine that they are often used with synchronized methods or blocks. A comparison between them and the operating system's interprocess communication mechanism reveals their similarity: synchronized methods or blocks provide functionality similar to the operating system primitives, and their execution is not interfered by multithreaded mechanisms, which are equivalent to block and wakeup Primitives (this pair of methods are declared as synchronized). Their combination enables us to implement a series of sophisticated interprocess communication algorithms (such as semaphore algorithms) on the operating system, and to solve the complex problem of communication between threads.
For the Wait () and notify () methods, the last two points are explained:
First: Calling the Notify () method causes the unblocked thread to be randomly selected from a thread that is blocked by calling the object's Wait () method, and we cannot anticipate which thread will be selected, so be particularly careful when programming to avoid problems with this uncertainty.
Second: In addition to notify (), there is also a method Notifyall () can also play a similar role, the only difference is that the call to the Notifyall () method will be blocked by calling the object's wait () method of all threads blocking all at once. Of course, only the thread that gets the lock can enter the executable state.
When it comes to obstruction, you can't stop talking about deadlocks, and a brief analysis can reveal that a suspend () method and a call to a wait () method that does not specify a time-out period can produce a deadlock. Unfortunately, Java does not support deadlock avoidance at the language level, and we must be careful to avoid deadlocks in programming.
We have analyzed the various methods of implementing thread blocking in Java, we have focused on the wait () and notify () methods, because they are the most powerful and most flexible to use, but this also leads to inefficient and error-prone. We should use various methods flexibly in practical use in order to achieve our goal better.
Seven: Daemon Threads
The daemon thread is a special kind of thread, it differs from the normal thread in that it is not a core part of the application, and when all the non-daemon threads of an application terminate, the application terminates even if there is still a daemon running, and conversely, as long as a non-daemon thread is running, the application will not terminate. A daemon is typically used to provide services to other threads in the background.
You can determine whether a thread is a daemon by calling Method Isdaemon (), or you can call method Setdaemon () to Cheng a line as a daemon.
Eight: Thread groups
Thread Group is a Java-specific concept, in Java, the thread group is a class-Threadgroup object, each thread is subordinate to the only one thread group, this thread group is specified when thread creation and the entire lifetime of the thread can not be changed. You can specify thread groups for the thread genus by calling the threads class constructor that contains the Threadgroup type parameter, and if not specified, the thread is subordinate to the system thread group named systems.
In Java, all thread groups must be explicitly created in addition to the pre-built system thread group. In Java, each thread group other than the system thread group is subordinate to another thread group, and you can specify the thread group that it belongs to when you create the thread group, and, if not, the system thread group by default. In this way, all the thread groups are composed of a tree rooted in the system thread group.
java allows us to manipulate all threads in a thread group at the same time, for example, by invoking the appropriate methods of the thread group to prioritize all of them, or to start or block all the threads in them.
Another important role of the Java thread group mechanism is thread safety. The threading group mechanism allows us to differentiate threads with different security features by grouping, to handle different groups of threads differently, and to support the adoption of unequal security measures through the hierarchical structure of thread groups. The Java Threadgroup class provides a number of ways to manipulate each thread group in the thread group tree and every thread in the thread group.
IX: Summary
In this article, we describe all aspects of Java multithreaded programming, including creating threads, and scheduling and managing multiple threads. We are acutely aware of the complexities of multithreading and the inefficiencies of multithreaded programs brought about by the cost of threading, which prompts us to think seriously about a problem: do we need multiple threads? When do I need multiple threads?
The core of multiple threads is the concurrent execution of multiple code blocks, which is essentially characterized in that the code between the blocks is executed in random order. Whether or not our program needs multiple threads is to see if it is also intrinsic.
If our program does not require multiple code blocks concurrent execution, that nature does not need to use multithreading; If our program requires multiple blocks of code to execute concurrently, but does not require chaos, then we can use a loop to achieve simple and efficient, and do not need to use multithreading , it is worthwhile to use multi-line Cheng when it fully conforms to the characteristics of multithreading, the strong support of multithreading for communication and thread management between threads.
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.