线程池详解
- 为什么要使用线程池呢?
线程池属于对象池,所有线程池都有一个非常重要的共性,就是为了最大程度的复用对象,因此,线程池最重要的特征就是利用线程。其次,java线程池的编程模型相对于原有的多线程模式来说,还有一大改进,那就是线程代码和业务代码的分离。 - 常用的线程池有几种?
newSingleThreadExecutor:单线程化的线程池
newCachedThreadPool:可缓存线程池
newFixedThreadPool:定长线程池
newScheduledThreadPool:可周期性执行线程池 - 线程池的主要处理流程
(1)单一数量的线程池
线程池 只有一个线程 并发量比较低 执行的时间很短
corePoolSize: 核心线程数 1
maximumPoolSize:最大线程数 1
keepAliveTime:空闲存活时间 0L
TimeUnit unit:单位 MILLISECONDS
workQueue:任务队列 LinkedBlockingQueue
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
(2)固定数量的线程池
线程池 内部线程数量是固定的
corePoolSize:核心线程数 传入参数 int n
maximumPoolSize:最大线程数 传入参数 int n
keepAliveTime:空闲存活时间 0L
TimeUnit unit:单位 MILLISECONDS
workQueue:任务队列 LinkedBlockingQueue
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
(3)可缓存的线程池
任务如果提交速率很快的话,会不停的创建线程
corePoolSize:核心线程数 0
maximumPoolSize:最大线程数 Integer.MAX_VALUE
keepAliveTime:空闲存活时间 60L
TimeUnit unit:单位 SECONDS
workQueue:任务队列 SynchronousQueue-----暂存队列
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
队列:先入先出 暂存队列 只有一个任务
空闲存活时间 达到之后会杀死掉没有使用的线程
(4)可周期性执行的线程池
规律 执行周期
提交任务后,先放进阻塞队列,待延时时间到达后在创建线程执行任务
corePoolSize:核心线程数 传入参数 int n
maximumPoolSize:最大线程数 Integer.MAX_VALUE
keepAliveTime:空闲存活时间 0
TimeUnit unit:单位 NANOSECONDS
workQueue:任务队列 DelayedWorkQueue
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
DelayedWorkQueue 阻塞队列-------优先级队列为底层结构
(1)延时判断
(2)对队列提交时间的排序
(5)自定义的线程池
- 线程优先级的特点:
线程优先级的一些特性:
(1)线程优先级的继承特性:也就是如果线程A启动线程B,那么线程A和B的优先级是一样的;
(2)线程优先级的规则性:即线程按照优先级的大小顺序执行,但是不一定是优先级较大的先执行完,因为线程的优先级还有下面第三个特性:
(3)线程优先级的随机特性 - 总结:
(1)线程优先级越高说明优先级越高。
(2)线程的默认级别是5,最高级别是10;
(3)大部分情况下在多个线程的情况下优先级高的先完成所有的任务。
(4)但是优先级并不是衡量那个线程运行结果顺序的标准,因为cpu只是尽量 将执行资源交给优先级高的线程。
注意:
优先级高的线程不一定每一次都先执行完run方法中的任务,也就是说线程优先级与打印顺序无关,他们的关系具有不确定性和随机性。 - 线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。
几种常见的线程同步方式:
1.volatile关键字解决缓存一致性和指令重排序问题。
2.加锁 synchronized、ReentrantLock、ReentrantReadWriteLock等。
3.线程安全的集合如ConcurrentHashMap、HashTable等。
4.线程安全的类如AtomicBoolean、AtomicInteger等。 - Thread和Runnable的区别和联系
Thread和Runable是我们创建多线程常见的方式方法
Thread的是一个类,Thread本身就实现了Runnable接口,当我们自定义多线程类时需要继承该接口
Runnable 是一个接口,该接口中只包含了一个run()方法,当我们自定义多线程类时时需要继承该接口并实现run方法
Runable接口和Thread类的区别主要包括以下几个方面
(1)线程类继承自Thread则不能继承其他类(单继承)、而Runable接口可以
(2)线程类继承自Thread相对于Runable来说,使用线程的方法更方便一些
(3)实现Runnable接口的线程类的多个线程、可以更方便的访问同一个变量,而Thread类需要内部类来进行替换 - 多次start一个线程会怎么样、线程有哪些状态。
start():作用是启动一个新线程。start需要首先调用,start不能被重复调用,而且start()内部调用了run()方法,新创建线程多次调用start方法将会报IllegalStateException异常
start和run方法的区别:
1、start方法内部会调用run方法。
2、当程序调用start方法,一个新线程将会被创建,并且在run方法中的代码将会在新线程上运行。
直接调用run方法的时候,程序并不会创建新线程,run方法内部的代码将在当前线程上运行。
3、一旦一个线程被启动,不能重复调用该thread对象的start方法,调用已经启动线程的start方法将会报IllegalStateException异常,但可以重复调用run方法。
本人才疏学浅,如有错误,烦请指出,谢谢!