已知线程有五大状态
(图来自:https://blog.youkuaiyun.com/u012403290/article/details/64910926?locationNum=11&fps=1)
新建状态:新建线程对象,并没有调用start()方法之前
就绪状态:调用start()方法之后线程就进入就绪状态,但是并不是说只要调用start()方法线程就马上变为当前线程,在变为当前线程之前都是为就绪状态。值得一提的是,线程在睡眠和挂起中恢复的时候也会进入就绪状态哦。
运行状态:线程被设置为当前线程,开始执行run()方法。就是线程进入运行状态
阻塞状态:线程被暂停,比如说调用sleep()方法后线程就进入阻塞状态
死亡状态:线程执行结束
问1:线程类的业务逻辑一般都在run方法中,为什么要调用start方法而不是run方法运行线程呢,运行run方法可以吗?
答:run方法和start方法都可以运行写在run方法体类的逻辑代码,但是调用run方法是当前线程(例如main方法)去执行这段逻辑代码,相当于还是一个人,调用run()后去做另外一件事情了,另外一件事情做完后,又回来继续做(main方法)这边的事情。而调用start()才是多线程的精髓。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
上面是Thread类的start()源码,可以看到这里先将当前线程加入到线程的group中(默认线程组,所有的线程都有线程组)。然后调用了start0(),这里的start0没有java代码。
在一位大神(https://www.linuxidc.com/Linux/2016-03/128997.htm)的博文中找到对start0的解释,这里应该是调用了java底层的natives方法,最后应该是调用了一个叫做JVM_StartThread的方法,按照名字来说应该让jvm去开启一个线程。所以start()做了什么已经很明显了。它调用了虚拟机,开启一个新的线程,去执行run。也就是,当前线程执行start()相当于,找来一个人,告诉他去做run()里面的事情,然后马上恢复到(main())这边的工作中,如果main中没有更多的逻辑,他就可以下班啦。
2.线程池有什么用,可以怎么用?
答:线程池与各种连接池一样,目的在是在于减少虚拟机频繁创建、销毁线程的性能损耗,需要线程,不需要创建,从线程池内拿,用完再扔回去,由线程池维护。
/**
* 线程池
*/
protected ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
threadPool.submit(thread);
最简单线程池代码创建和使用代码线程池维护线程数由系统核心数决定,关于各种线程池的区别的文章网上有很多,大家可以按照自己阅读理解习惯找来看。
但是在最新的阿里巴巴java开发手册中,明确标明了,不要用Executors创建线程池,在对性能要求比较高的系统中,还是不要使用Executors简单创建线程池的方法。
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
ThreadPoolExecutor的几种构造方法(利用Executors创建线程池其实也是调用了这边的方法):