一、简单粗暴的线程

  最原始的方式,当我们要并行的或者异步的执行一个任务的时候,我们会直接使用启动一个线程的方式,如下面所示:

new Thread(new Runnable() {

    @Override
public void run() {
// TODO Auto-generated method stub 这里放你要执行的方法
}
}).start();

  但是像上面或者类似这种每次来都是用new 一个Thread出来的方式存在着很多的弊端,如下面:

  • 每次new Thread新建对象性能差;
  • 线程缺乏统一的管理,可以无限制新建线程,相互之间竞争,还可能占用过多系统资源导致死机或者OOM(Out of Memory);
  • 缺乏更多的功能,如定时执行、定期执行、线程中断等。

二、线程池

  为了解决这些问题,Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。

1、作用

  根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

2、为什么要用线程池

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、定期执行、单线程、并发数控制等功能。
  • 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

3、主要的类

  Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

ExecutorService

真正的线程池接口。

ScheduledExecutorService

能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

  要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1)newSingleThreadExecutor

  创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2newFixedThreadPool

  创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3newCachedThreadPool

  创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

  那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4newScheduledThreadPool

  创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

三、实例

1)newCachedThreadPool
  创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}

  线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

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

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}

  因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
  定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

3)newScheduledThreadPool
  创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
}

  表示延迟3秒执行。

  定期执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);
}
}

  表示延迟1秒后每3秒执行一次。

4)newSingleThreadExecutor
  创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}

  结果依次输出,相当于顺序执行各个任务。

注意:以上的execute()方法可以替换为submit()方法,执行的结果是一样的。

四、submit()和execute()的区别

  JDK5往后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,它们的区别是:

  • execute(Runnable x) 没有返回值。可以执行任务,但无法判断任务是否成功完成。——实现Runnable接口
  • submit(Runnable x) 返回一个future。可以用这个future来判断任务是否成功完成。——实现Callable接口

致谢:感谢您的阅读!

java 线程池——异步任务的更多相关文章

  1. 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...

  2. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  3. Java线程池应用

    Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThread ...

  4. Java线程池与java.util.concurrent

    Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...

  5. Java线程池学习

    Java线程池学习 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java ...

  6. Java线程池使用和分析(一)

    线程池是可以控制线程创建.释放,并通过某种策略尝试复用线程去执行任务的一种管理框架,从而实现线程资源与任务之间的一种平衡. 以下分析基于 JDK1.7 以下是本文的目录大纲: 一.线程池架构 二.Th ...

  7. JAVA线程池应用的DEMO

    在做很多高并发应用的时候,单线程的瓶颈已经满足不了我们的需求,此时使用多线程来提高处理速度已经是比较常规的方案了.在使用多线程的时候,我们可以使用线程池来管理我们的线程,至于使用线程池的优点就不多说了 ...

  8. 这么说吧,java线程池的实现原理其实很简单

    好处 : 线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配.调优和监控,有以下好处: 1.降低资源消耗: 2.提高响应速度: 3.提高线 ...

  9. 由浅入深理解Java线程池及线程池的如何使用

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

随机推荐

  1. python中的Ellipsis

    ...在python中居然是个常量 print(...) # Ellipsis 看别人怎么装逼 https://www.keakon.net/2014/12/05/Python%E8%A3%85%E9 ...

  2. 05.GitHub实战系列~5.发布版本之分支操作+Tag讲解 2015-12-14

    GitHub实战系列汇总:http://www.cnblogs.com/dunitian/p/5038719.html ———————————————————————————————————————— ...

  3. MVC5 网站开发之七 用户功能 3用户资料的修改和删除

    这次主要实现管理后台界面用户资料的修改和删除,修改用户资料和角色是经常用到的功能,但删除用户的情况比较少,为了功能的完整性还是坐上了.主要用到两个action "Modify"和& ...

  4. spring boot(二):web综合开发

    上篇文章介绍了Spring boot初级教程:spring boot(一):入门篇,方便大家快速入门.了解实践Spring boot特性:本篇文章接着上篇内容继续为大家介绍spring boot的其它 ...

  5. 读书笔记--SQL必知必会05--高级数据过滤

    5.1 组合使用WHERE子句 操作符(operator)也称为逻辑操作符(logical operator),用来联结或改变WHERE子句中的过滤条件. 5.1.1 AND操作符 在WHERE子句中 ...

  6. ASP.NET Core 中文文档 第三章 原理(7)配置

    原文:Configuration 作者:Steve Smith.Daniel Roth 翻译:刘怡(AlexLEWIS) 校对:孟帅洋(书缘) ASP.NET Core 支持多种配置选项.应用程序配置 ...

  7. 利用Python进行数据分析(15) pandas基础: 字符串操作

      字符串对象方法 split()方法拆分字符串: strip()方法去掉空白符和换行符: split()结合strip()使用: "+"符号可以将多个字符串连接起来: join( ...

  8. Google地图开发总结

    我们经常使用地图查位置.看公交.看街景,同时地图还开放第三方的API给开发者.利用这些API进行地图的个性化的展示和控制,例如北京被水淹了,开发一个网页显示北京被淹的地图,地图上面标志被水淹的位置.严 ...

  9. GridView的使用(高度封装,不怎么灵活,repeat可替代)

    GridView的使用 首先,gridview是封装好的,直接在设计界面使用,基本不需要写代码: 一.绑定数据源 GridView最好与LinQDatasourse配合使用,相匹配绑定数据: 二.样式 ...

  10. asp.net core 简单部署

    目的 练习asp.net core的技术使用.部署等.目前拥有一台阿里云服务器(超级低配版本),安装了centos系统,打算将练习项目发布到该环境中.可能需要做以下准备工作. 以前没接触过linux正 ...