- 如何优雅的关闭线程与线程池?
关闭线程:
当 JVM 中不存在任何一个正在运行的非守护线程时,则 JVM 进程即会退出。对于在主线程中开辟的一个线程,setDaemon(true)将该线程设置为守护线程。当主线程退出时,JVM 会随之退出运行,守护线程同时也会被回收,即使里面是个死循环也不碍事。
守护线程拥有自动结束自己生命周期的特性,而非守护线程不具备这个特点。守护线程确保在程序退出时,或者说 JVM 退出时,线程能够自动关闭。
关闭线程池:
线程池状态变换图
- RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务;
- SHUTDOWN:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。
- STOP:不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用shutdownNow() 方法会使线程池进入到该状态;
- TIDYING:如果所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。
- TERMINATED:在terminated()方法执行完后进入该状态。
一般情形下关闭线程池:
// 线程池进入SHUTDOWN状态,停止接受新的任务
executorService.shutdown();
// 等待线程池任务完成
executorService.awaitTermination(30, TimeUnit.SECONDS);
在Spring中,如果线程池作为其他Bean中的属性,则需要在Bean的destroy时,关闭线程池
@PreDestroy
public void destroy() {
executorService.shutdown();
executorService.awaitTermination(30, TimeUnit.SECONDS);
}
- 使用线程池时如何注意队列过大造成的阻塞或OOM问题?
如下例所示,自定义线程池可以设置工作队列长度
executorService = new ThreadPoolExecutor(threadNum, threadNum, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(QUEUE_SIZE),
new BasicThreadFactory.Builder().namingPattern("process-%d").daemon(true).build());
在实际开发中,多线程里设置多线程,只需在最外层设置即可。
- 线程或线程池定义的位置如何选择?
Manager类中定义线程池作为其他Bean中的属性, 避免反复创建销毁消耗性能。
- Java多线程中向线程传递参数有哪几种方法?
(1)通过构造函数传递参数
public class MyThread1 extends Thread
{
private String name;
public MyThread1(String name)
{
this.name = name;
}
public void run()
{
System.out.println("hello " + name);
}
public static void main(String[] args)
{
Thread thread = new MyThread1("world");
thread.start();
}
}
(2)通过构造函数传递参数
public class MyThread2 implements Runnable
{
private String name;
public void setName(String name)
{
this.name = name;
}
public void run()
{
System.out.println("hello " + name);
}
public static void main(String[] args)
{
MyThread2 myThread = new MyThread2();
myThread.setName("world");
Thread thread = new Thread(myThread);
thread.start();
}
}
(3)通过回调函数传递数据
class Data
{
public int value = 0;
}
class Work
{
public void process(Data data, Integer numbers)
{
for (int n : numbers)
{
data.value += n;
}
}
}
public class MyThread3 extends Thread
{
private Work work;
public MyThread3(Work work)
{
this.work = work;
}
public void run()
{
java.util.Random random = new java.util.Random();
Data data = new Data();
int n1 = random.nextInt(1000);
int n2 = random.nextInt(2000);
int n3 = random.nextInt(3000);
work.process(data, n1, n2, n3); // 使用回调函数
System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+"
+ String.valueOf(n3) + "=" + data.value);
}
public static void main(String[] args)
{
Thread thread = new MyThread3(new Work());
thread.start();
}
}
- 其他坑:
- (1)任务提交后长时间没有执行
任务进入了队列,线程还在执行之前的任务。本质原因是对线程和队列的优先级认识不深刻,有一种错觉以为是所有线程都忙的时候才进入任务队列。实际上相反,是队列满的时候才会新建线程(线程数大于core size时)。
- (2)线程池中线程执行任务中无故消失(从日志可以看出,任务并未完成,也没有抛出异常)
一般情况下,代码中只会去捕捉RuntimeException,如果抛出Error则会导致线程退出,而异常信息又没有拿到。最佳的解决办法是给线程池设置UncaughtExceptionHandler
- 以构造函数的方式传入注解:
public class RunnableTask implements Runnable {
private int projectId;
private TransactionService transactionService; // 这个就是注解注入
RunnableTask() {
}
// 注解在别的Controller中
// @Autowired
// private TransactionService transactionService;
// 以构造参数的方式传递给Runnable即可
public RunnableTask(int projectId, TransactionService transactionService) {
this.projectId = projectId;
this.transactionService = transactionService;
}
@Override
public void run() {
...
}
7、如果try中没有异常,则顺序为try→finally,如果try中有异常,则顺序为try→catch→finally。但是当try、catch、finally中加入return之后
(1)finally中的代码总会被执行。
(2)当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。
(3)finally中有return时,会直接在finally中退出,导致try、catch中的return失效。
详解地址:https://www.cnblogs.com/pcheng/p/10968841.html
8、尽量不要在方法中return 线程,会报错,还会降低效率。