Java-线程池
理解池的概念
字符串常量池 String s1 = "abc"; String s2="abc"; s1==s2;
数据库连接池 Connection 每次链接创建,每次用完销毁 创建对象是要花费时间的
Integer -128---+127也有缓存池
线程池的概念
线程池是一种多线程处理形式,通过预先创建并管理一组线程,避免频繁创建和销毁线程的开销,提高系统性能和资源利用率。适用于需要处理大量短期任务的场景。
线程池的核心参数
- corePoolSize:核心线程数,线程池长期维持的线程数量。
- maximumPoolSize:最大线程数,任务队列满后允许创建的最大线程数。
- keepAliveTime:非核心线程闲置时的存活时间。
- workQueue:任务队列,用于保存待执行任务(如
ArrayBlockingQueue
、LinkedBlockingQueue
)。 - threadFactory:线程工厂,用于定制线程创建(如命名线程)。
- RejectedExecutionHandler:拒绝策略,处理任务队列满时的后续任务(如直接丢弃或抛出异常)。
线程池的工作流程
- 提交任务时,若当前线程数小于
corePoolSize
,立即创建新线程执行任务。 - 若线程数已达
corePoolSize
,任务被放入工作队列。 - 若队列已满且线程数未达
maximumPoolSize
,创建非核心线程执行任务。 - 若队列和线程数均达上限,触发拒绝策略。
线程池中的队列
线程池有以下工作队列:
ArrayBlockingQueue
LinkedBlockingQueue
线程池的拒绝策略
1.AbortPolicy 直接抛出异常
2.CallerRunsPolicy 拒绝后,由提交任务的线程执行此任务(如main线程)
3.DiscardOldestPolicy 丢弃队列中等待时间最长的那一个
4.DiscardPolicy 丢弃最后来的无法执行的任务
向线程池提交任务的两种方法
execute 与 submit 的区别
void execute 适用于不需要关注返回值的场景
submit 方法适用于需要关注返回值的场景。
关闭线程池
shutdownNow 立刻关闭,即使还有未执行完的任务
shutdown 等待所有任务执行完了再关闭
java中创建线程方式:
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
4.线程池
Java 提供的线程池实现
- FixedThreadPool:固定线程数,队列无界。
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
- CachedThreadPool:线程数无上限,空闲线程回收。
ExecutorService cachedPool = Executors.newCachedThreadPool();
- SingleThreadExecutor:单线程,确保任务顺序执行。
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
- ScheduledThreadPool:支持定时或周期性任务。
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3);
自定义线程池示例
推荐使用 ThreadPoolExecutor
构造函数灵活配置参数:
ThreadPoolExecutor customPool = new ThreadPoolExecutor(2, // corePoolSize4, // maximumPoolSize60, TimeUnit.SECONDS, // keepAliveTimenew ArrayBlockingQueue<>(100), // workQueuenew ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
线程ThreadLocal
作用: 为每个线程提供一个变量副本
使用:
//创建ThreadLocal对象,为每个线程自动的提供一个变量副本static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){@Overrideprotected Integer initialValue() {return 1; //初始化变量}};public static void main(String[] args) {/*这种方式在两个线程中对num进行操作,这个num是同一个*///线程1new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set(10);threadLocal.set(threadLocal.get()+5);System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());}}).start();//线程2new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set(20);threadLocal.set(threadLocal.get()+10);System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());}}).start();}
池的关闭
- shutdown():平滑关闭,不再接受新任务,等待已提交任务完成。
- shutdownNow():强制关闭,尝试中断所有线程并返回未执行任务列表。
ThreadLocal 内存泄漏问题
内存溢出: 内存不够用了
内存泄漏: 一些对象已经不再使用,但是虚拟机又不能回收的对象( 例如: 数据库连接对象,IO流对象,Socket 提供close)
如果使用ThreadLocal不当,会造成内存泄漏问题。
对象与引用关系
Object obj = new Object(); 强引用
obj= null; 没有引用
软引用: 被SoftReference对象管理的引用, 内存充足时,不回收该对象,一旦内存不足时,
就会回收软引用管理的对象.
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<Object>(o1);
弱引用: 被WeakReference对象管理的引用, 只要进行垃圾回收,就会被回收掉
ThreadLocal被弱引用管理的, 下次垃圾回收到来时,ThreadLocal会被回收掉, 造成ThreadLocalMap中的不存在了,
但是value还被外界引用, 所以ThreadLocalMap就不能被回收, 造成了内存泄漏.
所以, 正确的使用ThreadLocal方式是在用完之后, 主动删除ThreadLocalMap中的数据.
注意事项
- 避免使用无界队列(如
LinkedBlockingQueue
未指定容量),可能导致内存溢出。 - 根据任务类型(CPU密集型或IO密集型)合理设置线程数。
- 监控线程池状态(如通过
getActiveCount()
)。