In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
The editor today takes you to understand how to analyze Java multithreaded programming. The knowledge points in the article are introduced in great detail. Friends who feel helpful can browse the content of the article together with the editor, hoping to help more friends who want to solve this problem to find the answer to the problem. Follow the editor to learn more about "how to analyze Java multithreaded programming".
First, understand multithreading
Multithreading is a mechanism that allows multiple instruction streams to be executed concurrently in a program, each of which is called a thread and independent of each other. A thread, also known as a lightweight process, has independent execution control like a process, which is scheduled by the operating system. The difference is that the thread does not have an independent storage space, but shares a storage space with other threads in the process. This makes the communication between threads much easier than the process.
The execution of multiple threads is concurrent, that is, logically "simultaneous", regardless of whether it is physically "simultaneous" or not. If the system has only one CPU, then real "simultaneous" is impossible, but because CPU is so fast that users can't feel the difference, we don't need to care about it, we just need to imagine that each thread is executing at the same time.
The biggest difference in programming between multithread and traditional single thread is that because the control flow of each thread is independent of each other, the code between each thread is executed out of order, and the resulting thread scheduling, synchronization and other problems will be discussed in the future.
Second, implement multithreading in Java
Let's 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 to implement multithreading 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. This class provides a large number of ways for us to control our own threads, which we will discuss later.
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 called by the method start () of the Thread class and provides the code to be executed by our thread. To specify our own code, we just need to overwrite it!
Method 1: inherit the Thread class and override the method run (). We rewrite run () in the subclass of the created Thread class and add the code to be executed by the thread. 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; iTunes +) new MyThread (iTun1). Start ()
}
}
This method is simple and clear, in line with everyone's habits, however, it also has a big disadvantage, that is, if our class has inherited from a class (for example, Mini Program must inherit from the Applet class), we can no longer inherit the Thread class, then what if we do not want to create a new class?
We might as well explore a new approach: instead of creating a subclass of the Thread class, we use it directly, so we can only pass our method as an argument to an instance of the Thread class, which is a bit like a callback function. But Java does not have a pointer, so we can only pass an instance of the class that contains this method. So how do you restrict that this class must include this method? Use the interface, of course! (although abstract classes can be satisfied, they need inheritance, and the reason why we adopt this new approach is to avoid the limitations of inheritance. )
Java provides the interface java.lang.Runnable to support this approach.
Method 2: implement the Runnable interface
The Runnable interface has only one method run (), and we complete this part of the task by declaring that our class implements the Runnable interface and providing this method, writing our thread code into it. But the Runnable interface doesn't have any threading support, and we have to create an instance of the Thread class, which is done through the constructor public Thread (Runnable target); of the Thread class. 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; iTunes +) new Thread (new MyThread (iTun1)) .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 override the run method of the Thread class, otherwise the thread will execute the run method of the subclass instead of the run method of the class we use to implement the Runnable interface.
Using the Runnable interface to implement multithreading allows us to contain all the code in one class, which is good for encapsulation, but its disadvantage is that we can only use one set of code, and if we want to create multiple threads and make each thread execute different code, we still have to create additional classes, if so, in most cases it may not be as compact as directly inheriting Thread with multiple classes. To sum up, the two methods have their own advantages, and we can use them flexibly.
Let's take a look at some problems in the use of multithreading.
III. Four states of the thread
1. New state: the thread has been created but has not been executed (start () has not been called).
two。 Executable status: a thread can execute, although it is not necessarily executing. CPU time may be assigned to the thread at any time, causing it to execute.
3. Dead state: normally run () returns and causes the thread to die. Calling stop () or destroy () has the same effect, but it is not recommended, the former generates an exception, and the latter forces termination and does not release the lock.
4. Blocking state: threads are not allocated CPU time and cannot be executed.
IV. Priority of the thread
The priority of a thread represents the importance of the thread. When multiple threads are executable at the same time and waiting for CPU time, the thread scheduling system decides who to allocate CPU time according to the priority of each thread. The thread with higher priority has a greater chance of getting CPU time, and the thread with lower priority has a smaller chance.
You can call the Thread class methods getPriority () and setPriority () to access the thread's priority, which is between 1 (MIN_PRIORITY) and 10 (MAX_PRIORITY), and the default is 5 (NORM_PRIORITY).
Synchronization of threads
Because multiple threads of the same process share the same storage space, it not only brings convenience, but also brings the serious problem of access conflict. Java language provides a special mechanism to resolve this conflict, which effectively prevents the same data object from being accessed by multiple threads at the same time.
Since we can use the private keyword to ensure that data objects can only be accessed by methods, we only need to propose a mechanism for methods, which is the synchronized keyword, which includes two uses: synchronized methods and synchronized blocks.
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, and each synchronized method must obtain a lock of the class instance that calls the method before it can be executed, otherwise the thread it belongs to blocks, once the method is executed, the lock is monopolized, and the lock is not released until it is returned from the method, and then the blocked thread can acquire the lock and re-enter the executable state. This mechanism ensures that for each class instance, at most one of its member functions declared as synchronized is executable (because at most one can obtain the corresponding lock for the class instance), thus effectively avoiding access conflicts of class member variables (as long as all possible methods to access class member variables are declared as synchronized).
In Java, not only class instances, but also each class 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 drawback of the synchronized method: declaring a large method as synchronized will greatly affect efficiency. Typically, if you declare the thread class method run () as synchronized, because it has been running throughout the thread's lifetime, it will cause its calls to any synchronized methods of this class to never succeed. Of course, we can solve this problem by putting the code that accesses the class member variables into a special method, declaring it as synchronized, and calling it in the main method, but Java provides us with a better solution, which is the synchronized block.
2. Synchronized blocks: declare synchronized blocks through the synchronized keyword. The syntax is as follows:
Synchronized (syncObject) {
/ / allow access control code
}
A synchronized block is a block of code in which the code must acquire a lock on the object syncObject (which, as mentioned earlier, can be an instance of a class or a class) before it can be executed, with the same mechanism as described earlier. Because it can be targeted at any code block and the locked object can be specified arbitrarily, it is more flexible.
VI. Blocking of threads
In order to resolve access conflicts to the shared storage, Java introduced a synchronization mechanism. Now let's look at the access of multiple threads to shared resources. Obviously, the synchronization mechanism is not enough, because the resources required at any time may not be ready to be accessed, and conversely, 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 support for blocking mechanism.
Blocking refers to pausing the execution of a thread to wait for a condition to occur (such as a resource ready), which must be familiar to students who have studied the operating system. Java provides a number of ways to support blocking, so let's analyze them one by one.
1. The sleep () method: sleep () allows you to specify a period of time in milliseconds as a parameter, which makes the thread enter the blocking state within the specified time, and cannot get the CPU time. After the specified time, the thread returns to the executable state.
Typically, sleep () is used to wait for a resource to be ready: after the test finds that the condition is not met, let the thread block for a period of time and retest until the condition is met.
2. Suspend () and resume () methods: the two methods are used together. Suspend () makes the thread enter the blocking state and does not recover automatically. The corresponding resume () must be called in order to make the thread return to the executable state. Typically, suspend () and resume () are used to wait for the result produced by another thread: after the test finds that the result has not yet been produced, let the thread block, and after the other thread produces the result, call resume () to restore it.
3. The yield () method: yield () causes the thread to give up the current allocated CPU time, but does not block the thread, that is, the thread is still in an executable state and may be allocated CPU time again at any time. The effect of calling yield () is equivalent to the fact that the scheduler believes that the thread has executed enough time to go to another thread.
4. Wait () and notify () methods: two methods are used together, wait () makes the thread into a blocking state, it has two forms, one allows you to specify a period of time in milliseconds as a parameter, the other has no parameters, the former re-enters the executable state when the corresponding notify () is called or exceeds the specified time, while the latter must call the corresponding notify ().
At first glance, they are no different from the suspend () and resume () method pairs, but in fact they are very different. The core of the difference is that all of the methods described above do not release occupied locks (if occupied) when blocking, whereas the opposite is true.
The above core differences lead to a series of differences in details.
First of all, all the methods described above belong to the Thread class, but this pair belongs directly to the Object class, that is, all objects have this pair of methods. At first glance, this is incredible, but in fact it is natural, because when this pair of methods block, the occupied lock is released, and the lock is possessed by any object, calling the wait () method of any object causes the thread to block, and the lock on that object is released. Calling the notify () method of any object causes one of the randomly selected threads blocked by calling the object's wait () method to unblock (but not really executable until the lock is acquired).
Second, all the methods described earlier can be called anywhere, but this pair of methods must be called in the synchronized method or block, for the simple reason that only the current thread in the synchronized method or block holds the lock and the lock can be released. By the same token, the lock on the object that calls this pair of methods must be owned by the current thread so that the lock can be released. Therefore, this pair of method calls must be placed in a synchronized method or block whose locked object is the object that calls the pair of methods. If this condition is not met, the program can still be compiled, but an IllegalMonitorStateException exception will occur at run time.
The above characteristics of the wait () and notify () methods determine that they are often used with synchronized methods or blocks. Comparing them with the operating system's interprocess communication mechanism, we will find their similarities: synchronized methods or blocks provide functions similar to operating system primitives, and their execution will not be disturbed by multithreading mechanisms. This pair of methods is equivalent to block and wakeup primitives (both of which are declared as synchronized). The combination of them enables us to implement a series of exquisite inter-process communication algorithms (such as semaphore algorithm) on the operating system, and to solve a variety of complex inter-thread communication problems.
There are two last points about the wait () and notify () methods:
First: the thread that calls the notify () method to unblock is randomly selected from the thread that is blocked by calling the object's wait () method, and we can't predict which thread will be selected, so we should be very careful when programming to avoid problems caused by this uncertainty.
Second: in addition to notify (), there is another method notifyAll () that works similarly, except that calling the notifyAll () method unblocks all threads blocked by calling the object's wait () method at once. Of course, only the thread that acquired the lock can enter the executable state.
When it comes to blocking, you have to talk about deadlocks, and a little analysis shows that calls to both the suspend () method and the wait () method that does not specify a timeout period can cause deadlocks. Unfortunately, Java does not support deadlock avoidance at the language level, and we must be careful to avoid deadlocks in programming.
Above we have analyzed the various methods of implementing thread blocking in Java, and we focus on the wait () and notify () methods, because they are the most powerful and flexible to use, but this also leads to their inefficiency and error prone. In practice, we should use various methods flexibly in order to achieve our goal better.
7. Daemon thread
The daemon thread is a special kind of thread, which is different from the ordinary thread in that it is not the core part of the application. When all the non-daemon threads of an application stop running, even if there are still daemon threads running, the application will also be terminated. Conversely, as long as there is a non-daemon thread running, the application will not be terminated. Daemon threads are generally used to provide services to other threads in the background.
You can determine whether a thread is a daemon by calling the method isDaemon (), or you can call the method setDaemon () to set a thread as a daemon thread.
8. Thread group
Thread group is a unique concept of Java. In Java, a thread group is a ThreadGroup-like object, and each thread belongs to a unique thread group, which is specified at the time of thread creation and cannot be changed throughout the thread's lifetime. You can specify the thread group to which the thread belongs by calling the Thread class constructor that contains the ThreadGroup type parameter. If not specified, the thread belongs to the system thread group named system by default.
In Java, all thread groups must be created explicitly except for the pre-built system thread groups. In Java, each thread group other than the system thread group belongs to another thread group. You can specify the thread group to which it belongs when you create the thread group. If not specified, it belongs to the system thread group by default. In this way, all thread groups form a tree rooted in the system thread group.
Java allows us to operate on all threads in a thread group at the same time, for example, we can set the priority of all threads in the thread group by calling the corresponding methods of the thread group, or we can start or block all threads in it.
Another important role of Java's thread group mechanism is thread safety. Thread group mechanism allows us to distinguish threads with different security characteristics by grouping, to deal with threads in different groups differently, and to support the adoption of unequal security measures through the hierarchical structure of thread groups. Java's ThreadGroup class provides a number of methods to make it easy for us to manipulate every thread group in the thread group tree and every thread in the thread group.
The core of multithreading lies in the concurrent execution of multiple code blocks, and the essential characteristic is that the code between each code block is executed out of order. Whether our program needs multithreading or not depends on whether this is also its inherent feature.
If our program does not require multiple blocks of code to execute concurrently at all, then naturally there is no need to use multithreading; if our program requires multiple blocks of code to execute concurrently, but not out of order, then we can simply and efficiently implement it with a loop, and we don't need to use multithreading. Only when it fully conforms to the characteristics of multithreading, can the strong support of multithreading mechanism for inter-thread communication and thread management have the opportunity to give full play to its ability, and it is worthwhile to use multithreading.
Thank you for your reading, the above is "how to analyze Java multithreaded programming" all the contents, learn friends hurry up to operate it. I believe that the editor will certainly bring you better quality articles. Thank you for your support to the website!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.