Java multithreading learning

1、 Processes and threads

Process: each process has an independent code and data space (process context). Switching between processes will have a large overhead. A process contains 1 -- N threads.

Threads: the same kind of threads share code and data space. Each thread has an independent running stack and program counter (PC), and the thread switching overhead is small.

Like a process, a thread is divided into five stages: create, ready, run, block and terminate.

Multi process means that the operating system can run multiple tasks (programs) at the same time.

Multithreading means that there are multiple sequential streams executing in the same program.

2、 Method for realizing multithreading

To implement multithreading in Java, there are two methods: one is to continue the thread class, and the other is to implement the runable interface.

1. Inherit Java Lang. thread class

Output:

Run again:

Note: when the program starts and runs main, the Java virtual machine starts a process, and the main thread main is created when main() is called. With the start method of two objects of mitisay called, the other two threads are also started, so that the whole application runs under multithreading. be careful:

The multithreaded code is not executed immediately after the call of the start () method, Instead, it makes the thread runnable, and when to run is determined by the operating system. From the results of program running, it can be found that multithreaded programs are executed out of order. Therefore, only the code executed out of order needs to be designed as multithreaded. The purpose of thread. Sleep() method call is not to let the current thread occupy the CPU resources obtained by the process alone, To allow some time for other threads to execute. In fact, the execution order of all multithreaded code is uncertain, and the result of each execution is random. However, if the start method is called repeatedly, Java. Net will appear Lang.illegalthreadstateexception exception.

Output:

2. Implement Java Lang. runnable interface

Output:

explain:

Thread2 class has the characteristics of multi-threaded class by implementing runnable interface. The run () method is a convention of multithreaded programs. All the multithreaded codes are in the run method. The Thread class is actually a class that implements the Runnable interface. When we start the multithreading, we need to construct the object through the Thread class construction method Thread (Runnable target) and then invoke the start () method of the Thread object to run the multithreaded code. In fact, all multithreaded code is run by running the start () method of thread. Therefore, whether the thread class is extended or the runnable interface is implemented to realize multithreading, the thread is finally controlled through the API of the thread object. Being familiar with the API of the thread class is the basis of multithreading programming.

3、 The difference between thread and runable

If a class inherits thread, it is not suitable for resource sharing. However, if the runable interface is implemented, it is easy to realize resource sharing.

Output:

It can be seen from the above that the count is different between different threads, which will have a great problem for the ticket selling system. Of course, synchronization can be used here. Here, let's use runnable to take a look

Output:

Note that each thread uses the same instantiated object. If it is not the same, the effect will be the same as that above!

Summary: the advantages of implementing runnable interface over inheriting thread class: 1) it is suitable for multiple threads of the same program code to process the same resource; 2) it can avoid the limitation of single inheritance in Java; 3) it increases the robustness of the program, and the code can be shared by multiple threads, The code and data are independent to remind you that the main method is actually a thread. In Java, all threads are started at the same time. When and which one executes first depends entirely on who gets the CPU resources first. In Java, at least 2 threads are started each time the program runs. One is the main thread and the other is the garbage collection thread. Because whenever a class is executed with Java commands, a JVM is actually started, and each JVM starts a process in the operating system.

4、 Thread state transition

1. New: a thread object is newly created. 2. Runnable: after the thread object is created, other threads call the start () method of the object. The thread in this state is located in the runnable thread pool, becomes runnable, and waits to obtain the usage right of the CPU.   3、 Running: the thread in the ready state obtains the CPU and executes the program code. 4. Blocking state (blocked): the blocking state is that the thread gives up the CPU usage right for some reason and temporarily stops running. It does not have the opportunity to go to the running state until the thread enters the ready state. There are three kinds of blocking conditions: 4.1. Waiting for blocking: the running thread executes the wait() method, and the JVM will put the thread into the waiting pool. 4.2. Synchronization blocking: when a running thread obtains the synchronization lock of an object, if the synchronization lock is occupied by another thread, the JVM will put the thread into the lock pool. 4.3. Other blocking: when the running thread executes the sleep () or join () method, or makes an I / O request, the JVM will set the thread to the blocking state. When the sleep () state times out, the join () waits for the thread to terminate or time out, or the I / O processing is completed, the thread will return to the ready state. 5. Dead state: when the thread finishes executing or exits the run () method due to an exception, the thread ends its life cycle.

5、 Thread scheduling

1. Adjust thread priority: Java threads have priority, and threads with high priority will get more running opportunities. The priority of a java thread is expressed as an integer with a value range of 1 ~ 10. The thread class has the following three static constants:

The setpriority () and getpriority () methods of the thread class are used to set and obtain the priority of the thread respectively. Each thread has a default priority. The default priority of the main thread is thread NORM_ PRIORITY。 The priority of threads is inherited. For example, if thread B is created in thread a, then thread B will have the same priority as thread a.

The JVM provides 10 thread priorities, but it can't map well with common operating systems. If you want the program to be transplanted to each operating system, you should only use the thread class with the following three static constants as the priority, so as to ensure that the same priority adopts the same scheduling method. 2. Thread sleep: thread Sleep (long millis) method to make the thread go to the blocking state. The millis parameter sets the sleep time in milliseconds. When the sleep is over, it turns to the runnable state. The sleep () platform has good portability. 3. Thread waiting: the wait() method in the object class causes the current thread to wait until other threads call the notify() method or notifyall() method of the object to wake up. These two wake-up methods are also methods in the object class, and their behavior is equivalent to calling wait (0). 4. Thread: thread The yield () method pauses the thread object currently executing and gives the execution opportunity to threads with the same or higher priority. 5. Add a thread: join() method and wait for other threads to terminate. When the join () method of another thread is invoked in the current thread, the current thread is transferred to the blocking state until the end of the other process is completed, and the current thread is then changed from blocking to ready state. 6. Thread wakeup: the notify() method in the object class wakes up a single thread waiting on the object monitor. If all threads are waiting on this object, one of them will be selected to wake up. The choice is arbitrary and occurs when a decision is made about the implementation. The thread waits on the object's monitor by calling one of the wait methods. The awakened thread cannot continue executing until the current thread relinquishes the lock on this object. The awakened thread will compete with all other threads actively synchronized on the object in a conventional manner; For example, a awakened thread has no reliable privilege or disadvantage as the next thread to lock this object. A similar method is notifyall(), which wakes up all threads waiting on this object monitor. Note: the suspend () and resume () methods in thread are in jdk1 5 has been abolished and will not be introduced. Because it tends to deadlock.

6、 Description of common functions

1. Sleep (long millis): sleep (pause execution) the currently executing thread within the specified number of milliseconds. 2. Join(): wait for the T thread to terminate. Usage: join is a method of thread class. It is called directly after starting the thread, that is, the function of join () is to "wait for the thread to terminate". Here, it should be understood that the thread refers to the main thread waiting for the termination of the child thread. That is, the code after the child thread calls the join () method can only be executed when the child thread ends.

Why use the join () method:

In many cases, the main thread generates and starts a sub thread. If a large number of time-consuming operations are to be performed in the sub thread, the main thread will often end before the sub thread. However, if the main thread needs to use the processing results of the sub thread after processing other transactions, that is, the main thread needs to wait for the execution of the sub thread, The join () method will be used at this time. Without join:

Output results:

Add join:

Operation results:

The main thread must wait until all the sub threads are finished.

3. Yield(): pause the currently executing thread object and execute other threads.

  Thread. The yield () method is used to pause the currently executing thread object and execute other threads. What yield () should do is return the currently running thread to the runnable state to allow other threads with the same priority to get the chance to run. Therefore, the purpose of using yield () is to make threads with the same priority rotate appropriately. However, in practice, yield () cannot be guaranteed to achieve the purpose of concession, because the concession thread may also be selected again by the thread scheduler. Conclusion: yield() never causes a thread to go to a wait / sleep / block state. In most cases, yield () will cause the thread to go from running state to runnable state, but it may have no effect. See the picture above.

Operation results:

In the first case, Li Si (thread) will lose CPU time when it reaches 30, and Zhang San (thread) will seize CPU time and execute. In the second case, Li Si (thread) will lose CPU time when it reaches 30, and Li Si (thread) will seize CPU time and execute. The difference between sleep() and yield(), sleep() makes the current thread stagnant, Therefore, the thread executing sleep () will not be executed within the specified time; Yield () only returns the current thread to the executable state, so the thread executing yield () may be executed immediately after entering the executable state. The sleep method makes the currently running thread sleep for a period of time and enter the non running state. The length of this period of time is set by the program. The yield method makes the current thread give up the possession of the CPU, but the time given up is not settable. In fact, the yield () method corresponds to the following operations: first, check whether threads with the same priority are in the same runnable state. If so, give the possession of the CPU to this thread. Otherwise, continue to run the original thread. Therefore, the yield () method is called "concession", which gives the running opportunity to other threads with the same priority. In addition, the sleep method allows the thread with lower priority to get the chance to run, but when the yield () method is executed, the current thread is still in the runnable state. Therefore, it is impossible to give up the thread with lower priority to get the CPU possession later. In a running system, if the higher priority thread does not call the sleep method and is not blocked by I \ o, the lower priority thread can only wait for all the higher priority threads to run. 4. Setpriority(): change the priority of the thread.   MIN_ PRIORITY = 1     NORM_ PRIORITY = 5       MAX_ Priority = 10 usage:

5. Interrupt (): interrupt a thread. This method of ending is rough. If the T thread opens a resource and has not been closed in time, that is, the thread is forced to end before the run method is executed, which will cause the resource to be unable to be closed.

The best way to end the process is to use the sleep () function as in the example program. In the thread class, a boolean variable is used to control when the run () method ends. As soon as the run () method ends, the thread ends.   6、wait()   Obj. Wait (), and obj Notify () must be used together with synchronized (obj), that is, wait. With notify, it operates on the obj lock that has been obtained. From the perspective of syntax, it is obj wait(),Obj. Notify must be in synchronized (obj) {...} Within a statement block. Functionally, wait means that the thread actively releases the object lock after obtaining the object lock, and the thread sleeps at the same time. Until another thread calls notify() of the object to wake up the thread, it can continue to acquire the object lock and continue execution. The corresponding notify () is the wake-up operation on the object lock. However, it should be noted that after the notify () call, the object lock is not released immediately, but after the execution of the corresponding synchronized () {} statement block is completed. After the lock is automatically released, the JVM will randomly select a thread from the threads of the wait () object lock, give it an object lock, wake up the thread and continue to execute. This provides inter process synchronization and wake-up operations. Thread. Sleep() and object Both wait() can pause the current thread and release CPU control. The main difference is object Wait () releases the control of the object lock while releasing the CPU. It is not enough to understand the concept clearly. It needs to be tested in practical examples to better understand it. For object wait(),Object. The most classic example of the application of notify() should be the problem of printing ABC with three threads. This is a classic interview question. The requirements of the question are as follows: establish three threads, a thread prints 10 times, a thread prints 10 times, B thread prints 10 times, and C thread prints 10 times. Threads are required to run at the same time and print ABC alternately 10 times. This problem can be easily solved by using object's wait(), notify(). The code is as follows:

Output results:

First, let's explain the overall idea. From a general perspective, this problem is the synchronous wake-up operation between three threads. The main purpose is threada - > threadb - > threadc - > threada loop to execute three threads. In order to control the execution order of threads, it is necessary to determine the wake-up and waiting order. Therefore, each thread must hold two object locks at the same time before it can continue to execute. An object lock is prev, which is the object lock held by the previous thread. Another is its own object lock. The main idea is that in order to control the execution sequence, you must first hold the prev lock, that is, the previous thread must release its own object lock, and then apply for its own object lock. When both are available, print, and then call self Notify () releases its own object lock, wakes up the next waiting thread, and then calls prev Wait() releases the prev object lock, terminates the current thread, and waits for the loop to wake up again. Running the above code, you can find that three threads cycle to print ABC for 10 times. The main process of program running is that thread a runs first, holds the C and a object locks, releases the A and C locks, and wakes up B. Thread B waits for lock a, applies for lock B, prints B, releases lock B and lock a, wakes up C. thread C waits for lock B, applies for lock C, prints C, releases lock C and lock B, wakes up a. It seems that there is no problem, but if you think about it carefully, you will find that there is a problem. It is the initial condition. The three threads start in the order of a, B and C. according to the previous thinking, a wakes up B, B wakes up C, and C wakes up a. However, this assumption depends on the sequence of thread scheduling and execution in the JVM.

The differences between wait and sleep are: 1) both of them are in a multithreaded environment. They can block the specified number of milliseconds at the call of the program and return. 2) both wait () and sleep () can interrupt the thread's pause state through the interrupt () method, so that the thread can immediately throw an interruptedexception. If thread a wants to end thread B immediately, it can call the interrupt method on the thread instance corresponding to thread B. If thread B is waiting / sleep / join at the moment, thread B will immediately throw an interruptedexception and directly return in catch() {} to safely end the thread. It should be noted that the interruptedexception is thrown internally by the thread itself, not by the interrupt () method. When interrupt () is called on a thread, if the thread is executing normal code, the thread will not throw interruptedexception at all. However, once the thread enters wait () / sleep () / join (), it will throw interruptedexception immediately. Differences: 1) methods of thread class: sleep(), yield() and other object methods: wait() and notify(). 2) each object has a lock to control synchronous access. The synchronized keyword can interact with the lock of an object to synchronize threads. The sleep method does not release the lock, while the wait method releases the lock so that other threads can use the synchronization control block or method. 3) wait, notify and notifyAll can only be used in the synchronization control method or synchronization control block, while sleep can be used anywhere. 4) sleep must catch exceptions, while wait, notify and notifyAll do not need to catch exceptions. Therefore, the biggest difference between sleep() and wait() methods is that sleep() keeps the object lock during sleep, Still occupy the lock; When wait () sleeps, it releases the object lock. However, both wait () and sleep () can interrupt the thread's pause state through the interrupt () method, This enables the thread to throw interruptedexception immediately (but this method is not recommended). The sleep() method sleep() causes the current thread to enter a stagnant state (blocking the current thread) and gives up the use of cup. The purpose is not to let the current thread occupy the CPU resources obtained by the process alone, so as to leave a certain time for other threads to execute. Sleep() is the static of thread class (static) method; So he can't change the machine lock of the object, so when calling the Sleep () method in a Synchronized block, though the thread is dormant, but the object's machine lock and the wood have been released, other threads can not access the object (even if asleep, also has the object lock). After the sleep () sleep time expires, the thread will not be executed immediately. This is because other threads may be running and not scheduled to give up execution unless this thread has a higher priority. Wait () method the wait () method is a method in the object class; When a thread executes the wait () method, it enters a wait pool related to the object and loses (releases) the machine lock of the object (temporarily loses the machine lock, and the object lock needs to be returned after the wait (long timeout) timeout expires); Other threads can access; Wait () uses notify or notifyalll or specifies the sleep time to wake up the thread in the current waiting pool. Wiat () must be placed in the synchronized block, or "Java. Net" will be thrown in the program runtime Lang. illegalmonitorstateexception "exception.

7、 Explanation of common thread terms

Main thread: the thread generated by JVM calling program main(). Current thread: This is a confusing concept. Generally refers to through thread Currentthread(). Background thread: refers to the thread that provides services for other threads, also known as daemon thread. The garbage collection thread of the JVM is a background thread. The difference between the user thread and the daemon thread is that whether to wait for the main thread depends on the end of the main thread to end the foreground thread: it refers to the thread that receives the service of the background thread. In fact, the foreground and background threads are linked together, just like the puppet and the behind the scenes operator. The puppet is the foreground thread and the behind the scenes operator is the background thread. Threads created by foreground threads are also foreground threads by default. You can determine and set whether a thread is a background thread through isdaemon () and setdaemon () methods. Some common methods of thread class:

Sleep(): force a thread to sleep for n milliseconds. Isalive(): determines whether a thread is alive. Join(): wait for the thread to terminate. Activecount(): the number of active threads in the program. Enumerate(): enumerates threads in a program. Currentthread(): get the current thread. Isdaemon(): whether a thread is a daemon thread. Setdaemon(): set a thread as a daemon thread. (the difference between user thread and daemon thread is whether to wait for the main thread to end depends on the end of the main thread) setname(): set a name for the thread. Wait (): force a thread to wait. Notify (): notifies a thread to continue running. Setpriority(): sets the priority of a thread.

8、 Thread synchronization

The difficulty of Java multithreading is to deal with the communication between multiple threads during synchronization and concurrent runtime. When Java handles thread synchronization, the common methods are:

1. Synchronized keyword.

2. Lock displays locked.

3. Semaphore.

Thread synchronization problem introduction:

Create a bank account class, and add a dollar to the same account class instance after creating and starting 100 threads. Without using the above three methods:

Code:

Operation results:

The error is not generally obvious. The reason is very simple, that is, 100 threads modify the account at the same time. When the thread changes the money to 100, the second thread finally modifies it to 2. For these disobedient threads, take good care of them and let them be obedient. Before that, Still remember the following nouns:

Race state: when multiple threads access the same common resource and cause conflicts, resulting in thread asynchrony, we call this state race state.

Thread safety: for a class, if the object of a class does not cause contention in a multithreaded program, we call it thread safe. The above account is linearly unsafe. For example, StringBuffer is thread safe, while StringBuilder is thread unsafe.

Critical area: the reason for the contention state is that multiple threads enter a specific part of the program at the same time, such as the deposit method in the above program. We call this part the critical area in the program. To solve the problem of asynchronous multithreading, we need to solve how to make multiple threads access the critical area orderly.

1. Using the synchronized keyword

1. Synchronization method: add the synchronized keyword before the deposit method to make the method become a synchronization method: public synchronized void deposit (double amount) {}

2. Synchronization statement: add the synchronized keyword to a code block. Common format:

Where expr is the reference of the object. For example, the above program should be changed to this when calling deppoint method:

Using synchronized actually implicitly locks the method or code block. Any synchronized instance method can be converted into a synchronization statement:

Convert to synchronous statement:

2. Lock synchronization using lock

Java can also lock and unlock the critical area code displayed by lock, which is more intuitive and flexible than using the synchronized keyword.

A lock is an instance of a lock interface, which defines the method of locking and unlocking, and a lock can call its newcondition () method multiple times to create an instance named condition object for inter thread communication (used later).

With the lock interface, we also need to implement it. Java provides the renentrantlock class, which implements the lock interface to create mutually exclusive locks, so it's easy to do. Let's take a look at the figure in the book:

The code is as follows:

Give me a screenshot:

100 blocks are stored, and the time is significantly longer than before. 100 threads queue up to access the critical area. Also note that it is a good habit to call lock () followed by a try catch final statement.

3. Using semaphore synchronization

Semaphore is a good thing. Semaphore mechanism is widely used in operating system, such as Linux Process Synchronization semaphore. In Java, semaphore package contains some methods to access semaphore.

Semaphores can be used to limit the number of threads accessing shared resources. Before accessing resources in the critical area, threads must obtain a semaphore and return a semaphore after accessing. The following figure is about the semaphore class, which contains methods to access semaphores:

Using semaphore semaphore, you can change the above account class to:

There are many articles about Java multithreading on the Internet, and many of them have been written in series, but I think it is necessary to calm down and taste the interesting part like multithreading. The above three methods can solve the problem of multithreading synchronization.

Summary:

1. The purpose of thread synchronization is to protect the destruction of resources when multiple threads ask a resource.

2. Thread synchronization is realized through locks. Each object has only one lock. This lock is associated with a specific object. Once the thread obtains the object lock, other threads accessing the object can no longer access other asynchronous methods of the object.

3. For static synchronization methods, the lock is for this class, and the lock object is the class object of this class. The interlocking of static and non-static methods does not interfere. A thread obtains locks. When a synchronization method on another object is accessed in one synchronization method, it will obtain these two object locks.

4. For synchronization, you should always be aware of which object to synchronize on, which is the key.

5. When writing thread safe classes, you should always pay attention to making correct judgments on the logic and safety of multiple threads competing to access resources, analyze the "atomic" operation, and ensure that other threads cannot access competing resources during atomic operation.

6. When multiple threads wait for an object lock, the thread that does not obtain the lock will be blocked.

7. Deadlocks are caused by threads waiting for locks. In practice, the probability of occurrence is very small. Really let you write a deadlock program, not necessarily easy to use, ha ha. However, once the program deadlock occurs, the program will die.

9、 Thread data transfer

In the traditional synchronous development mode, when we call a function, we pass the data through the parameters of the function, and return the final calculation result through the return value of the function. However, in the multi-threaded asynchronous development mode, the data transmission and return are very different from the synchronous development mode. Because the running and ending of threads are unpredictable, it is impossible to return data through function parameters and return statements like functions when passing and returning data.

9.1 transfer data through construction methods. When creating a thread, you must create an instance of thread class or its subclass. Therefore, it is not difficult for us to think of passing data into the thread through the constructor of the thread class before calling the start method. And save the incoming data using class variables for the thread to use (in fact, it is used in the run method). The following code demonstrates how to pass data through a construction method:

Because this method transfers data while creating thread objects, the data will be in place before the thread runs, so that the data will not be transferred after the thread runs. If you want to pass more complex data, you can use data structures such as collections and classes. Although it is safe to use the construction method to transfer data, it will cause a lot of inconvenience if there is a lot of data to be transferred. Since Java does not have default parameters, overloading must be used to achieve the effect similar to the default parameters. This will not only make the construction method itself too complex, but also greatly increase the number of construction methods. Therefore, to avoid this situation, you have to pass data through class methods or class variables.

9.2 there are generally two opportunities to transfer data to objects through variables and methods. The first opportunity is to transfer data through construction methods when creating objects, Another opportunity is to define a series of public methods or variables (also known as fields) in the class. Then, after creating the object, assign values one by one through the object instance. The following code is a revision of mythread1 class, using a setname method to set the name variable:

9.3 data transfer through callback function

The two methods of passing data to threads discussed above are the most commonly used. However, both of these methods actively transfer data into the thread class in the main method. This is passive for threads to receive these data. However, in some applications, it is necessary to dynamically obtain data during thread operation. For example, three random numbers are generated in the run method of the following code, and then the sum of the three random numbers is calculated through the process method of work class, and the result is returned through the value of data class. As can be seen from this example, three random numbers must be obtained before returning value. In other words, this value cannot be passed into the thread class in advance.

reference resources:

https://www.cnblogs.com/LZYY/p/3959526.html

https://www.cnblogs.com/1020182600HENG/p/5939933.html

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>