多线程和线程池
多线程的两种最原始的创建方式
一、继承Thread类
extend Thread
二、实现Runnable接口
implement Runnable
然后重写run方法即可,实际上Thread也是实现了Runnable接口的。Thread可以大致看做成Runnable的子类,同时拥有start方法
三、启动线程
启动线需要用到Thread类的start方法
//继承Thread的方式
class ThreadDemo extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"------"+i);
}
}
}
public static void main(String[] args) {
//启动两个线程
new ThreadDemo().start;
new ThreadDemo().start;
}
//实现Runnable方式
class RunnableDemo implements Runnable{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"------"+i);
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();
//启动两个线程
new Thread(runnableDemo ).start;
new Thread(runnableDemo ).start;
}
}
}
线程池
线程池其实就是帮我们封装好的一些多线程类,如果需要用直接拿过来用。
比较常用的-线程池分类
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduledThreadPool
以上这些都可以通过Executors对象创建出来,并通过Executor接口里唯一的execute方法创建线程。
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0;i<5;i++){
executorService.execute(new Runnable() {
@Override
public void run() {
for(int j=0;j<20;j++){ try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-----"+j);
}
}
});
}
executorService.shutdown();
《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 new ThreadPoolExecutor 实例的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
注意:
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
ThreadExecutor和Executors的关系
我们来看一下Executors的源码
/**
* 创建固定数量线程的线程池
*
* 这是最后一个参数--阻塞队列调用的方法,长度是Integer.MAX_VALUE
* public LinkedBlockingQueue() {
* this(Integer.MAX_VALUE);
* }
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 创建只有一个线程的线程池
*
* 这是最后一个参数--阻塞队列调用的方法,长度也是Integer.MAX_VALUE
* public LinkedBlockingQueue() {
* this(Integer.MAX_VALUE);
* }
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
*创建一个缓冲线程池
*
* 这是最后一个参数--阻塞队列调用的方法
* public SynchronousQueue() {
* this(false);
* }
*
* 它的第二个参数,maximumPoolSize 为Integer.MAX_VALUE
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
*
* 创建一个可以在给定延迟后再执行或定期执行命令的线程池
*
* ScheduledThreadPoolExecutor 是 ThreadPoolExecutor 的子类,代码如下:
*
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor
implements ScheduledExecutorService {
//这是下面调用的构造方法,其实是调用了父类的构造方法,这些参数都是下面分析的参数
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
}
*
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
参数介绍:
corePoolSize
在线程池中保持的线程的数量,即使是这些线程没有被使用,除非设置了线程超时时间
maximumPoolSize
最大线程数量,当workQueue队列已满,放不下新的任务,再通过execute添加新的任务则线程池会再创建新的线程,线程数量大于corePoolSize但不会超过maximumPoolSize,如果超过maximumPoolSize,那么会抛出异常,如RejectedExecutionException。
keepAliveTime和unit
当线程池中线程数量大于workQueue,如果一个线程的空闲时间大于keepAliveTime,则该线程会被销毁。unit则是keepAliveTime的时间单位。
workQueue
阻塞队列,当线程池正在运行的线程数量已经达到corePoolSize,那么再通过execute添加新的任务则会被加到workQueue队列中,在队列中排队等待执行,而不会立即执行。
为什么说
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
再结合注释看Executors类中的传参就明白了!
Executors中创建线程池:
FixedThreadPool 和 SingleThreadExecutor 传入的最后一个参数阻塞队列 ”workQueue“,默认的长度是INTEGER.MAX_VALUE,而它们允许的最大线程数量又是有限的,所以当请求线程的任务过多线程不够用时,它们会在队列中等待,又因为队列的长度特别长,所以可能会堆积大量的请求,导致OOM。
CachedThreadPool 和 ScheduledThreadPool 它们的阻塞队列长度有限,但是传入的第二个参数maximumPoolSize 为Integer.MAX_VALUE,这就意味着当请求线程的任务过多线程不够而且队列也满了的时候,线程池就会创建新的线程,因为它允许的最大线程数量是相当大的,所以可能会创建大量线程,导致OOM。