0. Introduction
In the computer world, when people talk about concurrency, it means that a series of tasks are executed simultaneously in the computer. This is true if the computer has multiple processors or multicore processors, and if the computer has only one core processor then it is just a superficial phenomenon.
All modern operating systems allow tasks to be performed concurrently. You can read the message while listening to music and surfing the news, and we say this concurrency is process-level concurrency. And within the same process, there are also multiple tasks, which are called threads for concurrent tasks that run within the same process.
What we're going to talk about here is thread-level concurrency. Java provides the thread class, which enables us to run multiple threads in a single Java process, each of which performs different tasks to implement concurrency.
1. Creating Threads
In Java, we have 2 ways to create a thread:
- By inheriting the thread class directly, and then overriding the run () method.
- Build a class that implements the Runnable interface, and then create a thread class object and pass the Runnable object as a construction parameter
As you can see, both of the ways to create a thread require a new thread object, and you can say that a thread object represents a threading instance.
Because Java is single-inheritance, if we use the first way to create a thread, it forces only the thread to inherit, the flexibility is low, for the second way to create the thread does not have this problem, so we generally choose the second way to create the thread. Let's use the second creation method to give a simple example, first, implement the Runnable interface, Output 1 to 10:
public class Myrunnable implements Runnable {@Overridepublic void Run () {for (int i = 0; i <; i++) {SYSTEM.OUT.PR Intln (i);}}}
Then, create the main method, create a new thread and start:
public class Main {public static void Main (string[] args) {for (int i = 0; i <; i++) {New Thread (new Myrunnable ()). St Art ();}}}
You can see that using this method to create a thread, we created a total of two classes, a class implemented the Runnable interface, representing a running task, a class is a running class, in the main method of running the class, we use the newly defined runnable instance to create a new thread and run. As you can see, creating threads with this method is quite clear from the first: A class defines a task, a class runs a task, so it is recommended to create a thread this way.
2. Running Threads
The way to run a thread is very simple, we just call the start () method of the thread instance, and when we call the start () method, the Java virtual Opportunity creates a thread for us and then runs the task defined in the run () method in that thread to really implement multithreading.
Here, it must be emphasized that the start () method should be called instead of the Run () method, which calls the run () method directly, the virtual machine does not create a new thread to run the task, only executes the task on the current thread and cannot implement multithreading concurrency, so the start () method should be called.
3. Stop thread
Stopping a thread in Java is not easy compared to creating and running a thread. Although the thread class provides a stop () method for stopping a thread, the method has inherent insecurity. Terminating a thread with thread.stop will free all monitors it has locked. If any objects previously protected by these monitors are in an inconsistent state, the corrupted object will be visible to other threads, which could lead to arbitrary behavior, which has been labeled obsolete and should not be used by us.
3.1. "Cancellation requested" flag
So, how do we stop a thread? We can stop the thread by using the "request cancellation" flag. We define the flag in the task first, and then the task periodically looks at the flag, and if this flag is set, the task ends early. The following program uses this technique to persist the enumeration of primes until it is canceled, noting that to ensure that the process works reliably, the flag must be set to the volatile type:
public class Primegenerator implements Runnable {private final list<biginteger> primes = new Arraylist<bigintege R> ();p rivate volatile boolean cancelled;public void Run () {BigInteger p = biginteger.one;while (!cancelled) {p = P.next Probableprime (); synchronized (this) {Primes.add (P);}}} public void Cancel () {cancelled = true;} Public synchronized list<biginteger> get () {return new arraylist<biginteger> (primes);}}
Here, the Primegenerator class provides the Cancel () method, and when the method is called, the cancelled flag bit is set to true, then the while loop in the run () method ends and the entire thread ends.
3.2. Using interrupts to stop threads
Canceling a task with the "requested cancellation" flag there is a problem if the thread is performing a blocking task, then the thread will never detect the cancellation flag and therefore will never end.
When this happens, how do we end the thread? There is a partial blocking library approach that supports interrupts, and thread breaks are a collaborative mechanism through which a thread can notify another thread, telling it to stop the current work as appropriate or possible, and to perform other work instead.
We can modify the example above, instead of using ArrayList to store prime results, and use Blockingqueue to store, Blockingqueue's put () method is blocking, if you still use the "requested cancellation" flag of the end policy, and put () Method is blocked, then the method will never stop, and for this case we can use the method of detecting the interrupt flag bit to determine the end thread:
public class Primeproducer extends Thread { private final blockingqueue<biginteger> queue; Primeproducer (blockingqueue<biginteger> queue) { this.queue = queue; } public void Run () { try { BigInteger p = biginteger.one; while (! Thread.CurrentThread (). isinterrupted ()) queue.put (P = p.nextprobableprime ()); } catch ( Interruptedexception consumed) { /* allow thread to exit * /}} public void Cancel () { Interrupt (); }}
This time, the Cancle () method no longer sets the end flag bit, but instead calls interrupt () to break the thread. When the Cancel () method is called, the interrupt flag bit of the current thread will be set to the True,blockingqueue's put () method to respond to interrupts, return from the blocking state, return, the while statement detects that the interrupt bit is flagged, and then ends the while loop, End of the entire thread.
3.3. Blocking method that cannot respond to interrupts
If the blocking method can respond to interrupts, then we can use the above method to end the thread, but there are still some blocking methods in the Java class Library that are not able to respond to interrupts, including:
- Synchronous socket I/O in the java.io package
- java.io synchronous I/O in the package
- asynchronous I/O for selector
- Get a lock
Fortunately, for I/O streams, we can end the thread by closing the underlying I/O stream, and the following code shows an example of how to encapsulate a nonstandard cancel operation, Readerthread manages a socket connection that reads data synchronously from the socket, To end a user's connection or to shut down the server, Readerthread overwrites the interrupt method so that it can handle both standard interrupts and the underlying socket.
public class Readerthread extends Thread {private static final int BUFSZ = 512; Private final socket socket; Private final inputstream in; Public readerthread (socket socket) throws IOException {this.socket = socket; this.in = Socket.getinputstream (); } public void Interrupt () {try {socket.close (); } catch (IOException ignored) {} finally {super.interrupt (); }} public void Run () {try {byte[] buf = new Byte[bufsz]; while (true) {int count = In.read (BUF); if (count < 0) break; else if (Count > 0) processbuffer (buf, Count); }} catch (IOException e) {/* Allow thread to exit */}} public void Processbuffer (byte[] buf, int c ount) {}}
For this particular thread class, we call the interrupt () method, where the thread first closes the socket and then sets the interrupt flag bit in the finally (). After the socket is closed, the socket read in the run () method is immediately thrown, and the catch clause catches the exception and stops the thread successfully.
3.4. Summary
Finally, let me summarize the contents of the above:
The best way to end a thread is to let it end automatically, and if you want to end the run of the thread prematurely, you need to differentiate between three situations.
1, if there is no blocking method in the code, you can set an "end" flag bit, and then constantly detect it, when it is true, the active end of the thread;
2, if there is a blocking method in the code, and the method can respond to interrupts, then you can call the Thread.interput () end thread;
3. If there is a blocking method in your code that does not respond to interrupts, you need to end the thread by shutting down the underlying resource and letting the code throw an exception.
Java Concurrency (Fundamentals)-Create, run, and stop a thread