1. Scene repetition

Yesterday, a colleague of the company came to me in a hurry and told me that there was a very weird BUG in his project. I don’t know what happened.

Colleague: I use five threads to calculate students’ scores of each subject, finally summary, local are normal, but a test environment is less than a result, also did not throw an exception, what ghost?

Trick 7: What does the task thread do? Is the thread exception handled? Why not print a log? Soul triple, hahaha (just kidding, that’s not my style)

Oil 7: Ok, let’s take a look at the code first… After a scan, I have a rough idea of what’s going on.

Colleague: elder brother, I this procedure still have to save, client bottom dead order, solve today.

Submit submit to execute 5 minutes later…

Colleague: Oh my god, there is an exception thrown, I have a problem with the calculation logic here, 666, what is the reason, why I submit, the exception is not thrown?

Oil7: Well, the question… .

Second, program simulation

Because my colleague’s code logic is rather convoluted, it is not convenient for us to reproduce the problem, so I wrote a simple example of the problem as the basis for the analysis of this article. The ArithmeticException should be thrown when the divisor is 0.

Simulation code

The code is as follows:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ExceptionMissMain {

	public static class Task implements Runnable {
		String name;
		int a, b;

		public Task(String name, int a, int b) {
			this.name = name;
			this.a = a;
			this.b = b;
		}

		@Override
		public void run(a) {
			double c = a / b;
			System.out.println("Subject:" + name + ", achievement:"+ c); }}public static void main(String[] args) throws InterruptedException {
		ThreadPoolExecutor es = new ThreadPoolExecutor(5.5.0L, TimeUnit.MILLISECONDS, new SynchronousQueue<>());
		for (int i = 0; i < 5; i++) {
			es.submit(new Task(String.valueOf(i), 100, i));
			//es.execute(new Task(String.valueOf(i), 100, i));
			Thread.sleep(2000); }}}Copy the code

Results output

Submit way

Subject: 1, result: 100.0 Subject: 2, result: 50.0 Subject: 3, result: 33.0 Subject: 4, result: 25.0 Lack of a result, the program runs without exception thrownCopy the code

The execute method

Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero at com.tiny.juc.boot.pool.ExceptionMissMain$Task.run(ExceptionMissMain.java:30) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at Java. The base/Java. Lang. Thread. The run (Thread. Java: 830) subjects: 1, grade: 100.0 subjects: 2, grade: 50.0 subjects: 3, grade: 33.0 subjects: 4, grade: 25.0 the lack of a performance and program run exception is thrownCopy the code

Three, get to the bottom of it

Submit does not have an exception, execute does not have an exception.

Don’t tangle, directly start digging grave, take a look at the source code of two ways exactly how to achieve, not the truth come to light? Just do it! We use breakpoint debugging way, step by step to see the process of the program running.

The source pursuit

The execute implementation

1. First let’s look at the implementation of the execute method. It is found that the program normally enters the addWorker method

2. What does the addWorker method do? If we look at the following code, we can see that the addWorker method first creates a Worker object, and passes the incoming Runnable task to the new Worker, and then removes the Thread variable from the Worker object. Then call the thread start method of the current Worker. Question: What code does the start() method run? What does Worker object creation do? How are threads for Worker objects created?


3. With the question of the second step, let’s try again, this time into the New Worker and have a look. When the Worker object is created, it creates a thread as the target object and assigns it to the thread in the Worker. The Worker class implements the Runnable interface, so that means the t.start() method in the previous step. The run method of the target object Worker is called.

4. In order to verify the explanation in step 3, we set breakpoints on the run method in Thread class and the run method in Worker class respectively and run again. It was found that, as we expected, the program first entered the run method of Thread class, then called the run method of Worker class, and then called the runWorker method of Worker class.


5. Now, what does runWorker do? We find that runWorker gets the Runnable task of the Worker object (that is, the task we created) and calls the run method of our task.


6.OK, now we just need to look at the runWorker task.run() method calling the exception handling here. We found that we were running exception catching, we were trying to catch the Throwable exception and we were throwing it up, and our ArithmeticException, which divisor to zero, was included.


Note: the execute method throws an exception. What? I just had a good time, and you’re telling me to leave? Submit, why not throw exception ah, what situation has not said, do not want to slip… Okay, let’s move on to the underlying implementation of Submit.

Submit to realize

1. First of all, let’s look at the implementation of the submit method. We find that the program will convert the task submitted by us into FutureTask through the newTaskFor method

2. After the task is converted to FutureTask, the same execute method is invoked as before


Task.run () : = task.run() : = task.run() : = task.run() : = task.run(); To call the task we defined.

4. So let’s go to the FutureTask class and look at the implementation of the Run method. We found that the run method had a try catch exception and called the setException method, but in the setException method, the exception was assigned to the outcome, and no other processing was found.

5. Finally, we look at where the outcome appears in the entire Class of FutureTask and find that the outcome is returned in the GET method by calling the Report method.

6. So let’s go to the program and get it and see what happens. The result is the same exception as the execute method.

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
	at com.tiny.juc.boot.pool.ExceptionMissMain.main(ExceptionMissMain.java:39)
Copy the code

A submit submission does not raise an exception, but execute submits does.

Four,

1) Submit method, which calls setException output to the outcome in FutureTask after the exception information is captured;

2) If the task is submitted by submit method, futureTask’s GET method should be used to receive it;

3) The execute method throws the exception information of the task upwards.

4) When using thread pool, be careful, handle exceptions and record logs;

.