Java多线程(三)——线程池及定时器

简介: Java多线程(三)——线程池及定时器

线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。线程池就相当于预先创建好几个线程(招聘几个打工人),来分配之后要处理的任务(干活)

线程池的接口:ExecutorService

线程池对象

使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象

可以看到有7个参数,通过这些参数设置线程池的规模和特征。ExecutorService的常用方法有execute、submit、shutdown、shutdownNow。

  1. ThreadPoolExecutor构造器的参数:
  1. 1:指定线程池的线程数量(核心线程): corePoolSize
  2. 2:指定线程池可支持的最大线程数: maximumPoolSize
  3. 3:指定临时线程的最大存活时间: keepAliveTime
  4. 4:指定存活时间的单位(秒、分、时、天): unit
  5. 5:指定任务队列: workQueue
  6. 6:指定用哪个线程工厂创建线程: threadFactory
  7. 7:指定线程忙,任务满的时候,新任务来了怎么办: handler,默认丢弃任务并抛出RejectedExecutionException异常。
  1. ThreadPoolExecutor创建线程池对象
ExecutorService pool=new ThreadPoolExecutor(3,6,8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
  1. 线程池处理Runnable任务 ——pool.execute()

首先实现Runnable接口,重写run方法。然后创建MyRunnable任务对象,只不过这里不是把MyRunnable任务对象交给Thread处理,而是使用线程池pool的execute()方法。

class myRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " ——>" + i);
        }
        try {
            System.out.println(Thread.currentThread().getName() + " —— 休眠");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + " —— 启动");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//
        Runnable target=new myRunnable();
        pool.execute(target);
  1. 线程池处理Callable任务 ——pool.submit()

定义类实现Callable接口,重写call方法,封装要做的事情。然后把Callable对象进行submit(),并且可以返回执行后的结果。

class myCallable implements Callable<String> {
    private int n;
    public myCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int s=0;
        for (int i = 0; i < n; i++) {
            s+=n;
        }
        return Thread.currentThread().getName()+" 1+...+" + n +
                ", 子线程执行结果: "+s;
    }
}
//
        Callable myCallable=new myCallable(10);
        Future<String> f1 = pool.submit(myCallable);
        System.out.println(f1.get());

使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象

Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。Executors得到线程池对象的常用方法:

方法

说明

弊端

public static ExecutorService newCachedThreadPool()

线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。

允许创建的线程数量最大上限是Integer.MAX_VALUE,非常非常大

可能会创建大量线程,出现OOM错误( 内存溢出 java.lang.OutOfMemoryError )

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ExecutorService newSingleThreadExecutor ()

创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。

允许请求的任务队列长度是Integer.MAX_VALUE,可能会堆积大量请求,出现OOM错误

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。

允许创建的线程数量最大上限是Integer.MAX_VALUE,

可能会创建大量线程,出现OOM错误

Executors不适合做大型互联网场景的线程池方案,建议使用ThreadPoolExecutor来指定线程池参数,这样可以明确线程池的运行规则,规避资源耗尽的风险。

定时器

定时器是一种控制任务延时调用,或者周期调用的技术。定时器的实现方式有两种:

方式一:Timer。创建Timer定时器对象,然后开启定时器。这是一种单线程方法,处理多个任务按照顺序执行,存在延时与设置定时器的时间有出入。可能因为其中的某个任务的异常使Timer线程死掉,从而影响后续任务执行。

Timer t=new Timer();
    t.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器~");
            }
        },3000,3000);

方式二: ScheduledExecutorService定时器。基于线程池,某个任务的执行情况不会影响其他定时任务的执行。先得到线程池对象,然后再进行周期调度方法。

ScheduledExecutorService pool=Executors.newScheduledThreadPool(3);
        pool.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器");
            }
        },3,3,TimeUnit.SECONDS);

并发与并行

CPU同时可以处理线程的数量有限,所以CPU会轮询为每个线程服务,只是由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,其实可能只是并发。所以说多个线程其实是并发与并行同时进行的

线程的6种状态

NEW(新建)

线程刚被创建,但是并未启动。

Runnable(可运行)

线程已经调用了start()等待CPU调度。

Blocked(锁阻塞)

线程在执行的时候未竞争到锁对象,则该线程进入Blocked状态。

Waiting(无限等待)

一个线程进入Waiting状态,另一个线程调用notify或者notifyAll方法才能够唤醒。

Timed Waiting(计时等待)

同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。带有超时参数的常用方法有Thread.sleep 、Object.wait。

Teminated(被终止)

因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

相关文章
|
13天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
48 0
|
25天前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
59 16
|
2月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
2月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践
|
3月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
3月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
300 83
|
3月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
139 0
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
433 1
|
安全 算法 Java
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)(下)
17 Java多线程(线程创建+线程状态+线程安全+死锁+线程池+Lock接口+线程安全集合)
173 6