多线程----线程创建的四种方式

 

1.线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程,下图显示了一个线程完整的生命周期。

2.多线程使用场景
多线程,故名思议,就是一段程序上有多个线程在执行,由于CPU的快速切换,是的多个线程看似是同时执行的。常见的多线程应用场景如下所示:
1、servlet多线程。
2、数据库用到的多线程。
3、tomcat,tomcat内部采用多线程,上百个客户端访问同一个WEB应用,tomcat接入后就是把后续的处理扔给一个新的线程来处理,这个新的线程最后调用我们的servlet程序,比如doGet或者dpPost方法。
4、后台任务:如定时向大量(100W以上)的用户发送邮件;定期更新配置文件、任务调度(如quartz),一些监控用于定期信息采集。
5、自动作业处理:比如定期备份日志、定期备份数据库。
。。。。。

3.多线程的实现:

 

java中提供了三种多线程实现方式,分别为实现Runable接口,继承Thread类以及通过 Callable 和 Future 创建线程。
(1)通过实现 Runnable 接口来创建线程:
首先创建一个类实现Runable接口,并且重现run()方法,将要处理的逻辑加入其中

创建线程类,将实现RunableTest的实例作为参数传入其中。

运行结果:

(2)通过继承Thread来创建线程
编写测试类继承Thread类,重写run()方法

创建线程类,将继承Thread的实例作为参数传入其中,也可以直接实话ThreadClassTest类,然后调用父类的start方法启动线程

运行结果:

(3)通过 Callable 和 Future 创建线程
1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

 

 运行结果为:

 

三种实现多线程的方法优缺点比较:
1、继承Thread类有一个缺点就是单继承,而实现Runnable接口则弥补了它的缺点,可以实现多继承
2、继承Thread类必须如果产生Runnable实例对象,就必须产生多个Runnable实例对象,然后再用Thread产生多个线程;而实现Runnable接口,只需要建立一个实现这个类的实例,然后用这一个实例对象产生多个线程。即实现了资源的共享性
3.Callable接口实现多线程,其中的call方法可以抛出异常,运行callable可以拿到一个Future对象,Future对象表示异步计算的结果,他提供了检查计算是否完成的方法。

(4l)利用线程池来创建线程

线程池同连接池类似,我们可以从线程池中获得线程,并且同时对线程进行管理,以达到提高效率的目的。在实际的项目开发中,一般都是通过这种方式来实现多线程

1.传统方法创建线程的弊端:

每次都通过new关键字创建线程,性能较差。

线程之间缺乏统一的管理,容易造成系统宕机

不能提供例如:定时执行、定期执行、线程中断等操作的支持

 

有关ThreadPoolExecutor类
ThreadPoolExecutor类是线程池的核心类,内部抽象了一些列的方法,方便我们获取线程,以及对线程池进行设置和操作,具体如下:

前三中构造方法最终都会调用第四个构造方法,在构造方法中:

  • corePoolSize:该线程池中核心线程数最大值程,线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程
  • maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程,maximumPoolSize = 核心线程数 + 非核心线程数。
  • keepAliveTime:该线程池中非核心线程闲置超时时长,一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁掉
  • unit:参数keepAliveTime的时间单位。
  • workQueue:当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务
  • threadFactory:线程工厂,主要用来创建线程;(一般不用)
  • handler:表示当拒绝处理任务时的策略,即抛出异常。(一般不用)

以上参数的具体使用策略:
1.线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
2.线程数量达到了corePools,则将任务移入队列等待
3.队列已满,新建线程(非核心线程)执行任务
4.队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天(RejectedExecutionHandler)抛出异常
向ThreadPoolExecutor添加任务

 

常见的四种线程池

Java通过Executors提供四种线程池,分别为:
(1)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 

 

(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

运行结果:

注意:三个线程并不是按照顺序依次执行。
(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

posted @ 2018-11-29 15:37  老衲洗头爱飘柔  阅读(835)  评论(0编辑  收藏  举报