Table of contents(?)[-]
One extends the javalangThread class two implements the javalangRunnable interface three differences between Thread and Runnable four thread state transition five thread scheduling six common functions explaining how to use why join method is used seven common thread noun explanation eight thread synchronization nine thread data transmission
This article mainly talks about the usage methods of multi-threading in Java, thread synchronization, thread data transfer, thread status and corresponding thread function usage and overview.
First, let’s talk about the difference between a process and a thread:
Process: Each process has independent code and data space (process context), and switching between processes will have a large overhead. A process contains 1-n threads.
Thread: The same type 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, threads are divided into five stages: creation, ready, running, blocking, and terminating.
Multi-process means that the operating system can run multiple tasks (programs) at the same time.
Multithreading refers to multiple sequential streams being executed in the same program.
In Java, there are two ways to implement multi-threading. One is to continue the Thread class, and the other is to implement the Runable interface.
1. Extend the java.lang.Thread class
package com.multithread.learning;/** *@functon Multithreading Learning*@author Lin Bingwen*@time 2015.3.9 */class Thread1 extends Thread{ private String name; public Thread1(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "Run: " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } }} public class Main { public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); }} Output:
A run: 0
B run: 0
A run: 1
A run: 2
A run: 3
A run: 4
B run: 1
B run: 2
B run: 3
B run: 4
Run it again:
A run: 0
B run: 0
B run: 1
B run: 2
B run: 3
B run: 4
A run: 1
A run: 2
A run: 3
A run: 4
illustrate:
When the program starts and runs main, the java virtual machine starts a process, and the main thread main is created when the main() is called. With the start method of the two objects of MitiSay, the other two threads are also started, so that the entire application runs under multiple threads.
Note: The start() method is called not to execute multi-threaded code immediately, but instead makes the thread become a runnable state. When it runs is determined by the operating system.
From the results of the program running, we can find that multi-threaded programs are executed out of order. Therefore, only code executed out-of-order must be designed as multi-threaded.
The purpose of the Thread.sleep() method invocation is to prevent the current thread from occupying the CPU resources obtained by the process alone, so as to leave a certain amount of time for other threads to execute.
In fact, the execution order of all multi-threaded code is uncertain, and the results of each execution are random.
However, if the start method is called repeatedly, a java.lang.IllegalThreadStateException will occur.
Thread1 mTh1=new Thread1("A"); Thread1 mTh2=mTh1; mTh1.start(); mTh2.start(); Output:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Unknown Source)
at com.multithread.learning.Main.main(Main.java:31)
A run: 0
A run: 1
A run: 2
A run: 3
A run: 4
2. Implement the java.lang.Runnable interface
/** *@functon Multithreading Learning*@author Lin Bingwen*@time 2015.3.9 */package com.multithread.runnable;class Thread2 implements Runnable{ private String name; public Thread2(String name) { this.name=name; } @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "Run: " + i); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { new Thread(new Thread2("C")).start(); new Thread(new Thread2("D")).start(); }} Output:
C run: 0
D run: 0
D run: 1
C Run: 1
D run: 2
C run: 2
D run: 3
C run: 3
D run: 4
C run: 4
illustrate:
The Thread2 class implements the Runnable interface, which makes the class have the characteristics of a multi-threaded class. The run() method is a convention for multithreaded programs. All multi-threaded code is in the run method. The Thread class is actually a class that implements the Runnable interface.
When starting multi-threading, you need to first construct the object through the Thread class constructor Thread(Runnable target), and then call the start() method of the Thread object to run the multi-thread code.
In fact, all multithreaded code is run by running Thread's start() method. Therefore, whether it is to extend the Thread class or implement the Runnable interface to implement multi-threading, or ultimately control threads through the Thread object API, familiarizing yourself with the Thread class API is the basis for multi-thread programming.
3. The difference between Thread and Runnable
If a class inherits Thread, it is not suitable for resource sharing. However, if the Runable interface is implemented, it is easy to implement resource sharing.
package com.multithread.learning;/** *@functon Multi-thread learning, inherit Thread, resources cannot be shared*@author Lin Bingwen*@time 2015.3.9 */class Thread1 extends Thread{ private int count=5; private String name; public Thread1(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "Run count= " + count--); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } }}public class Main { public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); }} Output:
B run count= 5
A run count= 5
B run count= 4
B run count= 3
B run count= 2
B run count= 1
A run count= 4
A run count= 3
A run count= 2
A run count= 1
From the above, we can see that the counts are different between different threads, which will have a big problem for the ticket selling system. Of course, synchronization can be used here. Let's use Runnable to do it here
/** *@functon Multi-threading learning inherits runnable, resources can be shared*@author Lin Bingwen*@time 2015.3.9 */package com.multithread.runnable;class Thread2 implements Runnable{ private int count=15; @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "Run count= " + count--); try { Thread.sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } } public class Main { public static void main(String[] args) { Thread2 my = new Thread2(); new Thread(my, "C").start();//The same mt, but it is not possible in Thread. If you instantiate the object mt, an exception will appear new Thread(my, "D").start(); new Thread(my, "E").start(); }} Output:
C run count= 15
D run count= 14
E run count= 13
D run count= 12
D run count= 10
D run count= 9
D run count= 8
C run count= 11
E run count= 12
C run count= 7
E run count= 6
C run count= 5
E run count= 4
C run count= 3
E run count= 2
Here we should note that each thread uses the same instantiation object. If it is not the same, the effect will be the same as above!
Summarize:
The advantages of implementing the Runnable interface over inheriting the Thread class:
1): Suitable for multiple threads with the same program code to process the same resource
2): Can avoid the limitation of single inheritance in java
3): Increase the robustness of the program, the code can be shared by multiple threads, and the code and data are independent
Let me remind you: the main method is actually a thread. In Java, the threads are started at the same time. As for when and which one is executed first, it 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 using java commands, a JVM will actually be started, and each jVM internship starts a process in the operating system.
4. Thread state transition
1. New state (New): A new thread object is created.
2. Ready state (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 and becomes runnable, waiting to obtain the CPU usage rights.
3. Running state: The thread in the ready state acquires the CPU and executes the program code.
4. Blocked state: Blocked state means that the thread gives up the CPU usage rights for some reason and temporarily stops running. It is not until the thread enters the ready state that it has a chance to go to the running state. There are three types of blockage:
(1) Waiting to block: The running thread executes the wait() method, and the JVM will put the thread into the waiting pool.
(2) Synchronous blocking: When the running thread acquires the object's synchronization lock, if the synchronization lock is occupied by other threads, the JVM will put the thread into the lock pool.
(III), Other blocking: When a running thread executes the sleep() or join() method, or issues an I/O request, the JVM will set the thread to a blocking state. When the sleep() state timed out, join() waited for thread to terminate or timed out, or I/O processing was completed, the thread re-entered to the ready state.
5. Dead state: The thread has finished executing or exited the run() method due to an exception, and the thread ends its life cycle.
5. Thread Scheduling
Thread scheduling
1. Adjust thread priority: Java threads have priority, and threads with high priority will get more opportunities to run.
The priority of Java threads is represented by integers, with a value range of 1~10. The Thread class has the following three static constants:
static int MAX_PRIORITY
The highest priority that a thread can have is 10.
static int MIN_PRIORITY
The lowest priority that a thread can have is 1.
static int NORM_PRIORITY
The default priority assigned to the thread is 5.
The setPriority() and getPriority() methods of the Thread class are used to set and get 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 B will have the same priority as A.
The JVM provides 10 thread priorities, but it does not map well with common operating systems. If you want the program to be ported to each operating system, you should only use the Thread class with the following three static constants as priority, which can ensure that the same priority adopts the same scheduling method.
2. Thread sleep: Thread.sleep(long millis) method to make the thread go to a blocking state. The millis parameter sets the sleep time in milliseconds. When sleep is over, it becomes Runnable. The sleep() platform has good portability.
3. Thread wait: The wait() method in the Object class causes the current thread to wait until other threads call the object's notify() method or notifyAll() wake-up method. These two wake-up methods are also methods in the Object class, and their behavior is equivalent to calling wait(0).
4. Thread concessions: Thread.yield() method suspends the currently executing thread object and gives the execution opportunity to threads with the same or higher priority.
5. Thread join: join() method, waiting for other threads to terminate. Calling the join() method of another thread in the current thread, the current thread goes to a blocking state until the other process runs, and the current thread goes from blocking to ready state.
6. Thread wake-up: The notify() method in the Object class wakes up a single thread waiting for on this object monitor. If all threads are waiting on this object, one of the threads will be selected. The choice is arbitrary and occurs when making a decision on the implementation. The thread waits on the object's monitor by calling one of the wait methods. The waken thread cannot be executed until the current thread abandons the lock on this object. The waken thread will compete with all other threads that are actively synchronized on the object in a conventional manner; for example, the waken thread has no reliable privileges or disadvantages in being the next thread that locks this object. A similar method also has a notifyAll() that wakes up all threads waiting on this object monitor.
Note: The two methods suspend() and resume() in Thread have been abolished in JDK1.5 and will not be introduced again. Because there is a tendency to deadlock.
6. Description of common functions
① sleep(long millis): Let the currently executing thread sleep within the specified number of milliseconds (suspend execution)
②join(): refers to waiting for the t thread to terminate.
How to use it.
join is a method of the Thread class. It is called directly after starting the thread. That is, the function of join() is: "wait for the thread to terminate". What needs to be understood here is that the thread refers to the main thread waiting for the child thread to terminate. That is, the code after the child thread calls the join() method, and it can only be executed until the child thread is finished.
Thread t = new AThread(); t.start(); t.join();
Why use the join() method
In many cases, the main thread generates and starts the child thread. If a large number of time-consuming operations are required in the child thread, the main thread will often end before the child thread. However, if the main thread needs to use the child thread's processing result after processing of other transactions, that is, the main thread needs to wait for the child thread to complete execution before ending. At this time, the join() method must be used.
No join. /** *@functon Multithreading learning,join *@author Lin Bingwen*@time 2015.3.9 */package com.multithread.join;class Thread1 extends Thread{ private String name; public Thread1(String name) { super(name); this.name=name; } public void run() { System.out.println(Thread.currentThread().getName() + "Thread starts!"); for (int i = 0; i < 5; i++) { System.out.println("Subthread"+name + "Run: " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "Thread run ends!"); }}public class Main { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"Main thread run start!"); Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); System.out.println(Thread.currentThread().getName()+ "Main thread run ends!"); }} Output result:
Main main thread starts running!
Main main thread ends running!
B The thread run begins!
Child thread B runs: 0
A Thread run begins!
Child thread A runs: 0
Child thread B runs: 1
Child thread A runs: 1
Child thread A runs: 2
Child thread A runs: 3
Child thread A runs: 4
A Thread run ends!
Child thread B runs: 2
Child thread B runs: 3
Child thread B runs: 4
B The thread runs over!
Found that the main thread ended earlier than the child thread
Join
public class Main { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"Main thread run start!"); Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); try { mTh1.join(); } catch (InterruptedException e) { e.printStackTrace(); } try { mTh2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ "The main thread run ends!"); }} Running results:
Main main thread starts running!
A Thread run begins!
Child thread A runs: 0
B The thread run begins!
Child thread B runs: 0
Child thread A runs: 1
Child thread B runs: 1
Child thread A runs: 2
Child thread B runs: 2
Child thread A runs: 3
Child thread B runs: 3
Child thread A runs: 4
Child thread B runs: 4
A Thread run ends!
The main thread will definitely wait until the child threads are finished before it ends.
③yield(): Pauses the currently executing thread object and executes other threads.
The function of the Thread.yield() method is: pause the currently executing thread object and execute other threads.
What yield() should do is to get the current running thread back to runnable state to allow other threads with the same priority to get a run opportunity. Therefore, the purpose of using yield() is to allow threads of the same priority to perform appropriately. However, in reality, yield() cannot be guaranteed to achieve the purpose of concession, because the concession thread may be selected again by the thread scheduler.
Conclusion: yield() never causes the thread to go to the waiting/sleep/blocking state. In most cases, yield() will cause the thread to go from running state to run, but may not work. You can see the picture above.
/** *@functon Multithreading learning yield *@author Lin Bingwen*@time 2015.3.9 */package com.multithread.yield;class ThreadYield extends Thread{ public ThreadYield(String name) { super(name); } @Override public void run() { for (int i = 1; i <= 50; i++) { System.out.println("" + this.getName() + "-----" + i); // When i is 30, the thread will give up the CPU time and let other or its own threads execute (that is, whoever grabs it first executes it) if (i ==30) { this.yield(); } } }} public class Main { public static void main(String[] args) { ThreadYield yt1 = new ThreadYield("Zhang San"); ThreadYield yt2 = new ThreadYield("Li Si"); yt1.start(); yt2.start(); }} Running results:
The first case: Li Si (thread) will get the CPU time out when it is executed to 30. At this time, Zhang San (thread) grabs the CPU time and executes it.
The second situation: When Li Si (thread) executes to 30, the CPU time will be abandoned. At this time, Li Si (thread) grabs the CPU time and executes it.
The difference between sleep() and yield()
The difference between sleep() and yield()): sleep() causes the current thread to enter a stagnant state, so the thread executing sleep() will definitely not be executed within the specified time; yield() just causes the current thread to return to the executable state, so the thread executing yield() may be executed immediately after entering the executable state.
The sleep method causes the currently running thread to sleep for a period of time and enters an unrunable state. The length of this period is set by the program. The yield method allows the current thread to give up the CPU ownership, but the time of the transfer is unsettable. In fact, the yield() method corresponds to the following operation: first check whether there are threads with the same priority currently in the same runnable state. If so, hand over the CPU ownership to this thread, otherwise, continue to run the original thread. So the yield() method is called "concession", which gives the run opportunity to other threads with the same priority
In addition, the sleep method allows lower priority threads to get run opportunities, but when the yield() method is executed, the current thread is still in a runnable state, so it is impossible to give up lower priority threads to obtain CPU ownership later. In a running system, if the higher priority thread does not call the sleep method and is not blocked by I/O, then the lower priority thread can only wait for all the higher priority threads to run to have a chance to run.
④setPriority(): Change the priority of the thread.
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
usage:
Thread4 t1 = new Thread4("t1");
Thread4 t2 = new Thread4("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
⑤interrupt(): interrupt a thread. This ending method is rather rough. If the t thread opens a resource and has not had time to close it, that is, the run method is forced to end the thread before it has been executed, which will cause the resource to fail to close.
The best way to end the process is to use the example program of the sleep() function. A boolean variable is used in the thread class to control when the run() method ends. Once the run() method ends, the thread ends.
⑥wait()
Obj.wait() and Obj.notify() must be used with synchronized(Obj), that is, wait, and notify operate on the Obj lock that has been acquired. From a synchronized point of view, it is Obj.wait(), and Obj.notify must be in the synchronized(Obj){...} statement block. From a functional perspective, wait means that after the thread acquires the object lock, it actively releases the object lock, and the thread sleeps. The object lock can not be obtained and execution will continue until another thread calls the object notify() to wake up the thread. The corresponding notify() is the wake-up operation of the object lock. But one thing to note is that after the notify() call, the object lock is not released immediately, but the execution of the corresponding synchronized(){} statement block is completed and the lock is automatically released, the JVM will randomly select a thread from the wait() object lock thread, assign it to the object lock, wake up the thread, and continue execution. This provides synchronization and wake-up operations between threads. Both Thread.sleep() and Object.wait() can pause the current thread and release CPU control. The main difference is that while Object.wait() releases the CPU, it releases the control of the object lock.
It is not enough to understand conceptually, and it needs to be tested in practical examples to better understand. The most classic example of the application of Object.wait() and Object.notify() should be the problem of printing ABC with three threads. This is a relatively classic interview question, and the questions are as follows:
Establish three threads, thread A prints A 10 times, thread B prints B 10 times, thread C prints C 10 times, thread C requires thread to run at the same time, and ABC is printed alternately 10 times. This problem can be easily solved by using Object's wait() and notify(). The code is as follows:
/** * wait usage* @author DreamSea * @time 2015.3.9 */package com.multithread.wait;public class MyThreadPrinter2 implements Runnable { private String name; private Object prev; private Object self; private MyThreadPrinter2(String name, Object prev, Object self) { this.name = name; this.prev = prev; this.self = self; } @Override public void run() { int count = 10; while (count > 0) { synchronized (prev) { synchronized (self) { System.out.print(name); count--; self.notify(); } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws Exception { Object a = new Object(); Object b = new Object(); Object c = new Object(); MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a); MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b); MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c); new Thread(pa).start(); Thread.sleep(100); //Make sure to execute new Thread(pb).start(); Thread.sleep(100); } } Output result:
ABCABCABCABCABCABCABCABCABCABCABCABCABC
Let’s first explain its overall idea. From a general perspective, this problem is a synchronous wake-up operation between three threads. The main purpose is to execute three threads in ThreadA->ThreadB->ThreadC->ThreadA loop. In order to control the order of thread execution, the order of wake-up and wait must be determined, so each thread must hold two object locks at the same time before it can continue execution. An object lock is prev, which is the object lock held by the previous thread. Another one is the lock of the object. The main idea is that in order to control the order of execution, 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. Print when both are both. Then, first call self.notify() to release its own object lock, wake up the next waiting thread, and then call prev.wait() to release the prev object lock, terminate the current thread, and wait for the loop to be awakened again. Run the above code and you can find that three threads print ABC in a loop, a total of 10 times. The main process of program running is that thread A is the first to run, holds the object locks of C and A, and then releases the locks of A and C, and wakes up B. Thread B waits for lock A, then applies for lock B, then prints B, then releases B, A lock, wakes C, thread C waits for lock B, then applies for lock C, then prints C, then releases C, B lock, and wakes A. It seems that there is no problem, but if you think about it carefully, you will find that there is a problem, which is the initial condition. The three threads are started in the order of A, B, and C. According to the previous thoughts, A wakes up B, B wakes up C, C and then wakes up A. However, this assumption depends on the order of thread scheduling and execution in the JVM.
The difference between wait and sleep
Common points:
1. They are all in a multi-threaded environment, and can block the specified number of milliseconds at the program call and return.
2. Both wait() and sleep() can interrupt the thread's pause state through the interrupt() method, so that the thread immediately throws an InterruptedException.
If thread A wants to end thread B immediately, the interrupt method can be called on the Thread instance corresponding to thread B. If thread B is waiting/sleep/join at this moment, thread B will immediately throw an InterruptedException, and return it directly in catch() {} to safely end the thread.
It should be noted that the InterruptedException is thrown by the thread itself from the inside, not by the interrupt() method. When interrupt() is called on a thread, if the thread is executing normal code, the thread will not throw an InterruptedException at all. However, once the thread enters wait()/sleep()/join(), an InterruptedException will be immediately thrown.
Differences:
1. Thread class methods: sleep(), yield(), etc.
Object methods: wait() and notify(), etc.
2. Each object has a lock to control synchronous access. The Synchronized keyword can interact with the object's lock to realize thread synchronization.
The sleep method does not release the lock, while the wait method releases the lock, so that other threads can use synchronous control blocks or methods.
3. wait, notify and notifyAll can only be used in synchronization control methods or synchronization control blocks, while sleep can be used anywhere.
4. Sleep must catch exceptions, while wait, notify and notifyAll do not need to catch exceptions, so the biggest difference between sleep() and wait() methods is:
When sleep() sleeps, keep the object lock and still possesses the lock;
When wait() sleeps, the object lock is released.
However, wait() and sleep() can both interrupt the thread's pause state through the interrupt() method, so that the thread immediately throws an InterruptedException (but it is not recommended to use this method).
sleep() method
sleep() causes the current thread to enter a stagnant state (blocks the current thread), giving up the use of CUP, and the purpose is to prevent the current thread from occupying the CPU resources obtained by the process alone, so as to leave a certain time for other threads to execute;
sleep() is a Static method of the Thread class; therefore it cannot change the object's machine lock, so when calling the Sleep() method in a Synchronized block, although the thread is dormant, the object's machine lock is not released, and other threads cannot access the object (although it is still holding the object lock even when it is asleep).
After the sleep() sleep() sleep time expires, the thread does not necessarily execute immediately, because other threads may be running and are not scheduled to abandon execution unless the 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 waiting pool related to the object, and at the same time loses (releases) the object's machine lock (temporarily loses the machine lock, and the wait(long timeout) timeout time will also need to return the object lock); other threads can access it;
wait() uses notify or notifyAll or specified sleep time to wake up the thread in the current waiting pool.
wiat() must be placed in the synchronized block, otherwise the "java.lang.IllegalMonitorStateException" exception will be thrown when the program runtime.
7. Common thread terms explanation
Main thread: The thread generated by the JVM call program main().
Current thread: This is a confusing concept. Generally refers to a process obtained through Thread.currentThread().
Background thread: refers to a thread that provides services to other threads, also known as a daemon thread. The JVM garbage collection thread is a background thread. The difference between a user thread and a daemon thread is whether to wait for the main thread to end the foreground thread depending on the end of the main thread: it refers to the thread that accepts the service of the background thread. In fact, the foreground background threads are connected together, just like the relationship between the puppet and the behind-the-scenes manipulator. The puppet is the foreground thread, and the behind-the-scenes manipulator is the background thread. Threads created by foreground threads are also foreground threads by default. You can use isDaemon() and setDaemon() methods to determine and set whether a thread is a background thread.
Some common methods of thread classes:
sleep(): Force a thread to sleep in N milliseconds.
isAlive(): determines whether a thread survives.
join(): Wait for thread to terminate.
activeCount(): The number of active threads in the program.
enumerate(): Enumerate threads in the program.
currentThread(): Gets the current thread.
isDaemon(): Whether a thread is a daemon thread.
setDaemon(): Set a thread as a daemon thread. (The difference between a user thread and a daemon thread is whether to wait for the main thread to end depending on the end of the main thread)
setName(): Set a name for the thread.
wait(): Force a thread to wait.
notify(): Notify a thread to continue running.
setPriority(): Sets the priority of a thread.
8. Thread synchronization
1. There are two scopes of synchronized keywords:
1) It is within an object instance. Synchronized aMethod(){} can prevent multiple threads from accessing the synchronized method of this object at the same time (if an object has multiple synchronized methods, as long as one thread accesses one of the synchronized methods, other threads cannot access any synchronized methods in the object at the same time). At this time, the synchronized method of different object instances is uninterrupted. That is to say, other threads can still access the synchronized method in another object instance of the same class at the same time;
2) It is the scope of a certain class. Synchronized static aStaticMethod{} prevents multiple threads from accessing the synchronized static method in this class at the same time. It works on all object instances of a class.
2. In addition to using the synchronized keyword before the method, the synchronized keyword can also be used in a block in the method, indicating that only mutually exclusive access is performed on the resources of this block. The usage is: synchronized(this){/*block*/}, and its scope is the current object;
3. The synchronized keyword cannot be inherited. That is to say, the method of the base class synchronized f(){} is not automatically synchronized f(){} in the inherited class, but becomes f(){}. The inheritance class requires you to explicitly specify that one of its methods is synchronized;
Java's support and synchronization mechanism for multithreading is very popular. It seems that using the synchronized keyword can easily solve the problem of multithreaded shared data synchronization. What exactly? It is also necessary to have an in-depth understanding of the role of synchronized keywords before you can make a conclusion.
In general, the synchronized keyword can be used as a modifier of the function or as a statement within the function, which is the synchronization method and synchronization statement block that are usually mentioned. If you classify it more carefully, synchronized can act on instance variables, object references, static functions and class literals (class name literal constants).
Before we elaborate further, we need to clarify a few points:
A. Whether the synchronized keyword is added to a method or an object, the lock it acquires is an object, rather than treating a piece of code or function as a lock, and the synchronization method is likely to be accessed by objects from other threads.
B. Each object has only one lock associated with it.
C. Implementing synchronization requires a lot of system overhead as a cost and may even cause deadlocks, so try to avoid unnecessary synchronization control.
Next, let’s discuss the impact of synchronized using different places on the code:
Assuming that P1 and P2 are different objects of the same class, this class defines synchronization blocks or synchronization methods in the following situations, and P1 and P2 can call them.
1. When synchronized is used as a function modifier, the example code is as follows:
Public synchronized void methodAAA(){//….}This is the synchronization method. So which object is synchronized locked at this time? What it locks is to call this synchronous method object. That is to say, when an object P1 executes this synchronization method in different threads, mutual exclusion will be formed between them to achieve the effect of synchronization. However, another object P2 generated by Class to which this object belongs can arbitrarily call this method with the synchronized keyword added.
The above example code is equivalent to the following code:
public void methodAAA(){synchronized (this) // (1){ //…..}}(1) What does this at the point mean? It refers to the object that calls this method, such as P1. It can be seen that the synchronization method is essentially to apply synchronized to the object reference. Only the thread that has obtained the P1 object lock can call the P1 synchronization method. For P2, the P1 lock has nothing to do with it. The program may also get rid of the control of the synchronization mechanism in this situation, causing data confusion: (
2. Synchronize blocks, the sample code is as follows:
public void method3(SomeObject so) { synchronized(so){ //…..}}At this time, the lock is the object of so. Whoever gets the lock can run the code it controls. When there is a clear object as a lock, you can write the program like this, but when there is no clear object as a lock and just want a piece of code to synchronize, you can create a special instance variable (it has to be an object) to act as a lock:
class Foo implements Runnable{ private byte[] lock = new byte[0]; // Special instance variable Public void methodA(){ synchronized(lock) { //… }}//…..}Note: Zero-length byte array objects are more economical than any object to create a compiled byte code: only 3 opcodes are required to generate a zero-length byte[] object, while Object lock = new Object() requires 7 opcodes.
3. Use synchronized to static function, the example code is as follows:
Class Foo{public synchronized static void methodAAA() // 同步的static 函数{//….}public void methodBBB(){ synchronized(Foo.class) // class literal(类名称字面常量)} }代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
记得在《Effective Java》一书中看到过将Foo.class和P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。
可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
九、线程数据传递
在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。
9.1、通过构造方法传递数据在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据:
package mythread; public class MyThread1 extends Thread { private String name; public MyThread1(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { Thread thread = new MyThread1("world"); thread.start(); } }由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。
9.2、通过变量和方法传递数据
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置name变量:
package mythread; public class MyThread2 implements Runnable { private String name; public void setName(String name) { this.name = name; } public void run() { System.out.println("hello " + name); } public static void main(String[] args) { MyThread2 myThread = new MyThread2(); myThread.setName("world"); Thread thread = new Thread(myThread); thread.start(); } } 9.3、通过回调函数传递数据
上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个value是无法事先就传入线程类的。
package mythread; class Data { public int value = 0; } class Work { public void process(Data data, Integer numbers) { for (int n : numbers) { data.value += n; } } } public class MyThread3 extends Thread { private Work work; public MyThread3(Work work) { this.work = work; } public void run() { java.util.Random random = new java.util.Random(); Data data = new Data(); int n1 = random.nextInt(1000); int n2 = random.nextInt(2000); int n3 = random.nextInt(3000); work.process(data, n1, n2, n3); // 使用回调函数System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" + String.valueOf(n3) + "=" + data.value); } public static void main(String[] args) { Thread thread = new MyThread3(new Work()); thread.start(); } }以上就是对Java 多线程的详解,希望能帮助你学习这部分知识,谢谢大家对本站的支持!