8

Tôi muốn xử lý các ngoại lệ được đề xuất bởi các chuỗi công nhân theo phương thức ThreadPoolExecutor#afterExecute(). Hiện nay tôi có mã này:Tại sao ngoại lệ là null trong afterExecute() của ThreadPoolExecutor?

public class MyExecutor extends ThreadPoolExecutor { 

    public static void main(String[] args) { 
     MyExecutor threadPool = new MyExecutor(); 
     Task<Object> task = new Task<>(); 
     threadPool.submit(task); 
    } 

    public MyExecutor() { 
     super(4, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(4000)); 
    } 

    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     super.afterExecute(r, t); 
     System.out.println("in afterExecute()"); 
     if (t != null) { 
      System.out.println("exception thrown: " + t.getMessage()); 
     } else { 
      System.out.println("t == null"); 
     } 
    } 

    private static class Task<V> implements Callable<V> { 

     @Override 
     public V call() throws Exception { 
      System.out.println("in call()"); 
      throw new SQLException("testing.."); 
     } 
    } 
} 

Nếu tôi chạy đoạn code tôi nhận được kết quả:

in call() 
in afterExecute() 
t == null 

Tại sao là tham số Throwable tnull trong afterExecute()? Không phải là phiên bản SQLException?

Trả lời

7

Đây thực sự là hành vi được mong đợi.

Trích dẫn afterExecute Javadoc:

Nếu không null, các Throwable là RuntimeException còn tự do hay Lỗi gây ra thực hiện để chấm dứt đột ngột.

này có nghĩa là ví dụ Throwable sẽ RuntimeException hoặc Error, không kiểm tra Exception. Vì SQLException là ngoại lệ được kiểm tra, nó sẽ không được chuyển đến afterExecute.

Ngoài ra còn có cái gì khác xảy ra ở đây (vẫn trích dẫn Javadoc):

Lưu ý: Khi hành động được kèm theo trong nhiệm vụ (như FutureTask) hoặc rõ ràng hoặc thông qua các phương pháp như gửi, những các đối tượng nhiệm vụ nắm bắt và duy trì các ngoại lệ tính toán, và do đó chúng không gây ra chấm dứt đột ngột và các ngoại lệ bên trong không được chuyển cho phương thức này.

Trong ví dụ của bạn, tác vụ được đính kèm trong một FutureTask vì bạn đang gửi Callable, vì vậy bạn đang trong trường hợp này. Ngay cả khi bạn thay đổi mã của mình để ném một số RuntimeException, nếu không được cấp cho afterExecute. Javadoc đưa ra một mẫu mã để đối phó với điều này, mà tôi sao chép ở đây, để tham khảo:

protected void afterExecute(Runnable r, Throwable t) { 
    super.afterExecute(r, t); 
    if (t == null && r instanceof Future) { 
     try { 
     Object result = ((Future) r).get(); 
     } catch (CancellationException ce) { 
      t = ce; 
     } catch (ExecutionException ee) { 
      t = ee.getCause(); 
     } catch (InterruptedException ie) { 
      Thread.currentThread().interrupt(); // ignore/reset 
     } 
    } 
    if (t != null) 
     System.out.println(t); 
} 
+0

Cảm ơn bạn đã xóa thông tin đó. Vì vậy, tất cả các trường hợp ngoại lệ được kiểm tra sẽ chỉ được nuốt bởi ThreadPoolExecutor? Và tất cả xử lý ngoại lệ phải được thực hiện trong Callable # call()? –

+1

@ potato300 Xem chỉnh sửa của tôi, có điều gì đó khác xảy ra trong ví dụ cụ thể của bạn (mà tôi không nhận thấy lúc đầu) – Tunaki

1

Đây là một cách thay thế để làm việc đó. Lấy gợi ý từ here

package com.autonomy.introspect.service; 

import java.sql.SQLException; 
import java.util.concurrent.*; 

public class MyExecutor extends ThreadPoolExecutor { 

    public static void main(String[] args) { 
     MyExecutor threadPool = new MyExecutor(); 
     Task<Object> task = new Task<Object>(); 
     Future<Object> futureTask = threadPool.submit(task); 
     try { 
      System.out.println(futureTask.get()); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } catch (ExecutionException e) { 
      System.out.println("exception thrown: " + e.getMessage()); 
     } 
    } 

    public MyExecutor() { 
     super(4, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(4000)); 
    } 

    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     super.afterExecute(r, t); 
     System.out.println("in afterExecute()"); 
     if (t != null) { 
      System.out.println("exception thrown: " + t.getMessage()); 
     } else { 
      System.out.println("t == null"); 
     } 
    } 

    private static class Task<V> implements Callable<V> { 

     @Override 
     public V call() throws Exception { 
      System.out.println("in call()"); 
      throw new SQLException("testing.."); 
     } 
    } 
} 

Cách sử dụng afterExecute là cho một mục đích khác.

This class provides protected overridable beforeExecute(java.lang.Thread, 
java.lang.Runnable) and afterExecute(java.lang.Runnable, 
java.lang.Throwable) methods that are called before and after execution of 
each task. These can be used to manipulate the execution environment; for 
example, reinitializing ThreadLocals, gathering statistics, or adding log 
entries. Additionally, method terminated() can be overridden to perform any 
special processing that needs to be done once the Executor has fully 
terminated. 

If hook or callback methods throw exceptions, internal worker threads may 

lần lượt không thành công và đột ngột chấm dứt.

Các vấn đề liên quan