The previous section covered the basics of threads and processes, but for Java, threads are probably more than that, so the next series of articles will focus on threads.
Create a thread
The way threads are created is a cliche and one of the most popular interview questions. There is a lot of talk on the web about implementing a Runnable interface and implementing a Callable interface. This is not a mistake, but it depends on the perspective. But this kind of detail actually does not need to care too much, do not get to a dead end.
Implement the Runnable interface
Implement the Runnable interface and then override the run() method, which defines how and what the thread does.
public class ImplementsRunnable implements Runnable {
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + "------Runnable thread working...");
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
new Thread(newImplementsRunnable()).start(); }}}Copy the code
In the main method, 50 threads are started. To start a Thread is to create a new Thread and pass in the class that implements the Runnable interface. Now let’s see what happens
It can be seen that although we create threads in order, the order of execution of threads is controlled by the CPU, which can be said to be uncontrollable, and it is in this way that the multithreading is running.
Implement Callable interface
What needs to be rewritten once the interface is implemented is the call() method
public class ImplementsCallable implements Callable {
@Override
public Object call(a) {
System.out.println(Thread.currentThread().getName() + "--------callable thread working");
return "Implement callable, return value";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
for (int i = 0; i < 50; i++) {
ImplementsCallable callable = new ImplementsCallable();
FutureTask<String> task = new FutureTask<String>(callable);
newThread(task).start(); System.out.println(task.get()); }}}Copy the code
It is worth noting that there are no overloaded constructors with Callable arguments in the Thread class; they are basically Runnable
FutureTask implements the RunnableFuture interface, which in turn inherits Runnable and Future.
The main difference between the Runnable interface and the Callable interface is that the Callable interface has a return value. What are the scenarios for this return value? For example, in the CASE of an HTTP call, a result needs to be returned. How do I get the return value, using the get() method in FutureTask, and see what happens
There is an interesting problem here. When I comment line 14 and run it, I get the following result: the thread is disordered, which is exactly what I expect.
However, when I keep line 14 of code for several times, the following result will appear, the thread has become orderly, if there is a friend who knows why can leave a message
Thread class inheritance
After inheriting Thread, Idea doesn’t even have to be reminded to override the run() method manually
The overall code is as follows
public class ExtendsThread extends Thread{
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + "-------- Thread inheriting Thread working");
}
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
newExtendsThread().start(); }}}Copy the code
The code is relatively simple, so let’s take a look at the result, as expected
The implementation interface is preferred because Java does not support multiple inheritance. If you inherit Thread, you cannot inherit other classes, but you can implement multiple interfaces. And inheriting the entire Thread class is bloated in terms of performance overhead.
Common thread methods
There are many methods related to threads. Here are four common methods.
start
Start () is used every time a thread is started. What is the difference between run() and start()
public class CommonMethod {
public static void main(String[] args) throws InterruptedException {
startRunExample();
}
//start,run
public static void startRunExample(a) {
new MyThread().start();
newMyThread().run(); }}class MyThread extends Thread {
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + " is running"); }}Copy the code
Create a new class, then create an inner class that inherits Thread, calls the start() and run() methods, and calls the wrapped methods inside the main function. Let’s see what happens.
You can see that one thread name is the main thread and one is the child thread, so the start() method starts a thread that then executes the contents of the run() method. But if you use the run() method directly, the main thread simply executes the contents of the run() method without starting a new thread.
sleep
Sleep allows the current thread to sleep, freeing the CPU for other threads to execute.
public class CommonMethod {
public static void main(String[] args) throws InterruptedException {
// startRunExample();
sleepExample();
// yieldExample();
// waitExample();
}
/ / omit start, run
//sleep
public static void sleepExample(a) throws InterruptedException {
new MyThread().start();
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + " is running"); }}class MyThread extends Thread {
@Override
public void run(a) {
System.out.println(Thread.currentThread().getName() + " is running"); }}Copy the code
For example, if I start a new Thread, but I let the main Thread sleep for 3s before running, the result should be thread0 is running and 3s after main is running.
For comparison, I’ll comment out the sleep code and look at the result a few more times
You can see that the results of the two threads are coming out almost at the same time, but which one comes before and which one comes after is out of our control in this case.
yield
Yield is when a programmer suggests that the computer cede CPU usage from the current thread to another thread, but if the CPU doesn’t care about us, it’s a different matter. In plain English, it transitions from Running state to Runnable state.
Again, it is recommended that the computer suspend the current thread and execute another thread, but whether or not to do so is up to the computer.
Create another inner class YieldThread
public class CommonMethod {
public static void main(String[] args) throws InterruptedException {
// startRunExample();
// sleepExample();
yieldExample();
// waitExample();
}
/ / omit start, run
/ / omit the sleep
//yield
public static void yieldExample(a) throws InterruptedException {
YieldThread yieldThread = new YieldThread();
Thread thread = new Thread(yieldThread, "thread1");
Thread thread1 = new Thread(yieldThread, "thread2"); thread.start(); thread1.start(); }}class YieldThread extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is running " + i);
if (Thread.currentThread().getName().equals("thread1")) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " yield "+ i); }}}Copy the code
This is not an exact example, but you can see that the logic of a run is that each thread runs 10 times, producing a running and a yield each time. But when we add thread.yield (), the expected result is
thread1 is running
thread2 is running
thread2 yield
thread1 yield
Copy the code
Thread1 yields the running statement to the CPU. The CPU selects the logic of thread2 and thread1 yields the running statement to the CPU
And then let’s see if we can do what we expect
As you can see, only part of the result is expected when we remove the thread.yield () line
Yes, you’ll find this happening occasionally, but not as often. Because the two threads might be running in parallel, rather than concurrently (alternating), both execute the RUNNING statement at the same time, followed by yield from thread 2 and yield from thread 1.
Here is not necessarily accurate, so it is not quite accurate examples, if there is a better understanding and examples can leave a message!!
wait
The following example is a little more controllable than the previous yield, which is a suggestion, and the latter can be said to be coercive. Suspends the thread from Running to Block and does not execute until it is woken up by an external force.
public class CommonMethod {
public static void main(String[] args) throws InterruptedException {
// startRunExample();
// sleepExample();
// yieldExample();
waitExample();
}
/ / omit start, run
/ / omit the sleep
/ / omit yield
//wait
public static void waitExample(a) {
WaitThread waitThread = new WaitThread();
Thread thread1 = new Thread(waitThread, "thread1");
Thread thread2 = new Thread(waitThread, "thread2"); thread1.start(); thread2.start(); }}class WaitThread extends Thread {
@Override
public void run(a) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is running " + i);
if (Thread.currentThread().getName().equals("thread1")) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
Copy the code
The logic is the same, except that thread.yield () is replaced with wait(). Normally a Thread named thread1 executes once and then stops executing. Let’s see what happens
And expected results are the same, and also an error Java lang. IllegalMonitorStateException.
Creation is not easy, if it is helpful to you, welcome to like, collect and share!