1、线程池使用,提交任务时:
- 使用submit提交子任务,一定要获取返回值Future,通过get方法获取可能出现的异常,并且可以进行捕获(推荐)
- 使用execute执行子任务,异常可以被抛出,但是主线程不能捕获子任务线程中的异常
- 使用submit提交子任务,只是提交,不获取返回值future,异常会被封装在子线程内部,不会抛出,主线程也无法捕获
2、构建线程池时,推荐使用ThreadPoolExecutor:
- 线程池推荐使用Executors去创建,而是通过ThreadPoolExecutor的方式,
- 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
- 说明:Executors各个方法的弊端:
- * 1)newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
- * 2)newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
----摘自阿里编程规范
3、代码
1、使用execute提交任务,异常可抛出,主线程不能捕获
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 测试线程池异常(识别线程池使用时的一些坑)
* 1、使用submit提交子任务,一定要获取返回值Future,通过get方法获取可能出现的异常,并且可以进行捕获(推荐)
* 2、使用execute执行子任务,异常可以被抛出,但是主线程不能捕获子任务线程中的异常
* 3、使用submit提交子任务,只是提交,不获取返回值future,异常会被封装在子线程内部,不会抛出,主线程也无法捕获
*/
public class TestThreadPoolException
{
public static void main(String[] args)
{
int size = 2;
/**
* 线程池推荐使用Executors去创建,而是通过ThreadPoolExecutor的方式,
* 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
* 说明:Executors各个方法的弊端:
* 1)newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
* 2)newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
// 此处没有使用ThreadPoolExecutor的后两个参数,则使用的是默认线程工厂和默认的拒绝策略(AbortPolicy--抛出RejectedExecutionException)
ExecutorService executor = new ThreadPoolExecutor(0, size, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(size));
try
{
for (int i = 0; i < size; i++)
{
try
{
final int cur = i;
// Future获取子线程异常,外层能捕获future包装后的异常
// Future<?> future = executor.submit(new Runnable()
// execute执行中的异常能抛出,但是不能被外层中的try...catch...捕获
executor.execute(new Runnable()
// 只用submit,不获取submit的返回值-future,有异常也不会抛出
// executor.submit(new Runnable()
{
@Override
public void run()
{
Thread.currentThread().setName("Thread-" + cur);
System.out.println("Current value: " + cur);
System.out.println(Thread.currentThread().getName());
if (cur < 1)
{
return;
}
else
{
Integer.parseInt("abc");
}
}
});
// future.get();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Task exception.");
}
}
}
finally
{
executor.shutdown();
}
}
}
输出:
Current value: 0
Thread-0
Current value: 1
Thread-1
Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: "abc"
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at com.huawei.cloudsop.whatifservice.diff.test.TestThreadPoolException$1.run(TestThreadPoolException.java:63)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
2、使用submit,不获取submit的返回值-future,有异常也不会抛出
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 测试线程池异常(识别线程池使用时的一些坑)
* 1、使用submit提交子任务,一定要获取返回值Future,通过get方法获取可能出现的异常,并且可以进行捕获(推荐)
* 2、使用execute执行子任务,异常可以被抛出,但是主线程不能捕获子任务线程中的异常
* 3、使用submit提交子任务,只是提交,不获取返回值future,异常会被封装在子线程内部,不会抛出,主线程也无法捕获
*/
public class TestThreadPoolException
{
public static void main(String[] args)
{
int size = 2;
/**
* 线程池推荐使用Executors去创建,而是通过ThreadPoolExecutor的方式,
* 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
* 说明:Executors各个方法的弊端:
* 1)newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
* 2)newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
// 此处没有使用ThreadPoolExecutor的后两个参数,则使用的是默认线程工厂和默认的拒绝策略(AbortPolicy--抛出RejectedExecutionException)
ExecutorService executor = new ThreadPoolExecutor(0, size, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(size));
try
{
for (int i = 0; i < size; i++)
{
try
{
final int cur = i;
// Future获取子线程异常,外层能捕获future包装后的异常
// Future<?> future = executor.submit(new Runnable()
// execute执行中的异常能抛出,但是不能被外层中的try...catch...捕获
// executor.execute(new Runnable()
// 只用submit,不获取submit的返回值-future,有异常也不会抛出
executor.submit(new Runnable()
{
@Override
public void run()
{
Thread.currentThread().setName("Thread-" + cur);
System.out.println("Current value: " + cur);
System.out.println(Thread.currentThread().getName());
if (cur < 1)
{
return;
}
else
{
Integer.parseInt("abc");
}
}
});
// future.get();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Task exception.");
}
}
}
finally
{
executor.shutdown();
}
}
}
输出:
Current value: 0
Thread-0
Current value: 1
Thread-1
3、使用Future框架(推荐)-Future获取子线程异常,外层能捕获future包装后的异常
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 测试线程池异常(识别线程池使用时的一些坑)
* 1、使用submit提交子任务,一定要获取返回值Future,通过get方法获取可能出现的异常,并且可以进行捕获(推荐)
* 2、使用execute执行子任务,异常可以被抛出,但是主线程不能捕获子任务线程中的异常
* 3、使用submit提交子任务,只是提交,不获取返回值future,异常会被封装在子线程内部,不会抛出,主线程也无法捕获
*/
public class TestThreadPoolException
{
public static void main(String[] args)
{
int size = 2;
/**
* 线程池推荐使用Executors去创建,而是通过ThreadPoolExecutor的方式,
* 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
* 说明:Executors各个方法的弊端:
* 1)newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
* 2)newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
*/
// 此处没有使用ThreadPoolExecutor的后两个参数,则使用的是默认线程工厂和默认的拒绝策略(AbortPolicy--抛出RejectedExecutionException)
ExecutorService executor = new ThreadPoolExecutor(0, size, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(size));
try
{
for (int i = 0; i < size; i++)
{
try
{
final int cur = i;
// Future获取子线程异常,外层能捕获future包装后的异常
Future<?> future = executor.submit(new Runnable()
// execute执行中的异常能抛出,但是不能被外层中的try...catch...捕获
// executor.execute(new Runnable()
// 只用submit,不获取submit的返回值-future,有异常也不会抛出
// executor.submit(new Runnable()
{
@Override
public void run()
{
Thread.currentThread().setName("Thread-" + cur);
System.out.println("Current value: " + cur);
System.out.println(Thread.currentThread().getName());
if (cur < 1)
{
return;
}
else
{
Integer.parseInt("abc");
}
}
});
future.get();
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Task exception.");
}
}
}
finally
{
executor.shutdown();
}
}
}
输出:
Current value: 0
Thread-0
Current value: 1
Thread-1
java.util.concurrent.ExecutionException: java.lang.NumberFormatException: For input string: "abc"
at java.util.concurrent.FutureTask.report(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at com.huawei.cloudsop.whatifservice.diff.test.TestThreadPoolException.main(TestThreadPoolException.java:67)
Caused by: java.lang.NumberFormatException: For input string: "abc"
at java.lang.NumberFormatException.forInputString(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at java.lang.Integer.parseInt(Unknown Source)
at com.huawei.cloudsop.whatifservice.diff.test.TestThreadPoolException$1.run(TestThreadPoolException.java:63)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Task exception.
4、结论
1、使用线程池提交任务时,使用Future框架,可轻松获取执行结果,并在外层捕获异常
2、构建线程池时,使用ThreadPoolExecutor,尽量指定阻塞队列的大小
3、使用thread时,可使用Thread.setUncaughtExceptionHandler(xxx)处理线程异常