Java并发编程——线程池初步

概述:

线程池机制是事先创建一些线程等待服务端程序的调用,这些线程保存在一个数组结构中,称为“线程池”。当服务器有任务执行时,就从线程池中取出一个线程并给其分配任务,当线程任务执行完成后,再被放回线程池中。

优点:

1. 由于在任务到达之前,线程已经存在,所以这里为系统消除了线程创建的资源和时间的开销。可以立即为请求服务,使程序响应更快。

2. 通过适当地调节线程池中的线程数目,就强制使一些新到的任务处于等待状态,可以防止资源不足。

参考:

http://www.cnblogs.com/dolphin0520/p/3932921.html

《Java语言程序设计》——清华大学出版社

示例及分析:

MyTask.java

public class MyTask implements Runnable {

	private int taskNum;

	public MyTask(int num) {
		this.taskNum = num;
	}

	@Override
	public void run() {
		System.out.println("正在执行task " + taskNum);

		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("task " + taskNum + "执行完毕");
	}

}

上面的程序很简单,就只是去打印一些简单的信息。

Executor.java

public class Executor {

	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));

		for (int i = 0; i < 15; i++) {
			MyTask myTask = new MyTask(i);
			if (!executor.isShutdown()) {
				executor.execute(myTask);
			}

			printExecutor(executor);
		}

		executor.shutdown();
	}

	private static void printExecutor(ThreadPoolExecutor executor) {
		System.out.println("线程池中线程数目:" + executor.getPoolSize()
				+ ",队列中等待执行的任务数目:" + executor.getQueue().size()
				+ ",已执行完别的任务数目:" + executor.getCompletedTaskCount());
	}
}

上面用到了java为我们提供的类ThreadPoolExecutor,这是一个很好的类。上面用到创建实例时用到的构造器如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

它包含了下面这只个参数:

int corePoolSize:线程池中线程数

int maximumPoolSize:线程池中最大的线程数

long keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止

TimeUnit unit:参数keepAliveTime的时间单位,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

BlockingQueue<Runnable> workQueue:一个阻塞队列,用来存储等待执行的任务

<pre name="code" class="java">ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

上面代码中使用的是ArrayBlockingQueue阻塞队列,并传入了参数5。这里有一点需要注意一下,如果我们假设所有任务数为mTaskCount(即代码中的15),那么我们最好去满足这样一个公式:maximumPoolSize >= mTaskCount - corePoolSize,不然就会抛出一个java.util.concurrent.RejectedExecutionException异常。当然,你也可以有一个通用的行为:将maximumPoolSize设置为Integer.MAX_VALUE。

还有一种方式可以补救:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5, 200, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

不过目前可能大家对ThreadPoolExecutor的使用可能也不那么频繁了。因为Java为我们封装好了一些更方便的创建方式:

ExecutorService executor1 = Executors.newFixedThreadPool(5); // 创建固定容量大小的缓冲池
ExecutorService executor2 = Executors.newSingleThreadExecutor(); // 创建容量为1的缓冲池
ExecutorService executor3 = Executors.newCachedThreadPool(); // 创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE

下面是上面三种创建方式对应的结果输出:

创建固定容量大小的缓冲池

正在执行task 0
正在执行task 2
正在执行task 4
正在执行task 1
正在执行task 3
task 4执行完毕
正在执行task 5
task 0执行完毕
正在执行task 6
task 2执行完毕
正在执行task 7
task 1执行完毕
正在执行task 8
task 3执行完毕
正在执行task 9
task 5执行完毕
task 7执行完毕
正在执行task 10
task 6执行完毕
正在执行task 11
正在执行task 12
task 9执行完毕
正在执行task 13
task 8执行完毕
正在执行task 14
task 11执行完毕
task 12执行完毕
task 10执行完毕
task 13执行完毕
task 14执行完毕

创建容量为1的缓冲池

正在执行task 0
task 0执行完毕
正在执行task 1
task 1执行完毕
正在执行task 2
task 2执行完毕
正在执行task 3
task 3执行完毕
正在执行task 4
task 4执行完毕
正在执行task 5
task 5执行完毕
正在执行task 6
task 6执行完毕
正在执行task 7
task 7执行完毕
正在执行task 8
task 8执行完毕
正在执行task 9
task 9执行完毕
正在执行task 10
task 10执行完毕
正在执行task 11
task 11执行完毕
正在执行task 12
task 12执行完毕
正在执行task 13
task 13执行完毕
正在执行task 14
task 14执行完毕

创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE

正在执行task 0
正在执行task 2
正在执行task 4
正在执行task 6
正在执行task 8
正在执行task 10
正在执行task 12
正在执行task 14
正在执行task 1
正在执行task 3
正在执行task 5
正在执行task 9
正在执行task 7
正在执行task 11
正在执行task 13
task 2执行完毕
task 8执行完毕
task 6执行完毕
task 4执行完毕
task 0执行完毕
task 10执行完毕
task 14执行完毕
task 12执行完毕
task 1执行完毕
task 3执行完毕
task 5执行完毕
task 7执行完毕
task 9执行完毕
task 13执行完毕
task 11执行完毕

RejectedExecutionException异常:

http://blog.csdn.net/wzy_1988/article/details/38922449

时间: 2024-10-18 12:19:52

Java并发编程——线程池初步的相关文章

Java并发编程——线程池的使用

在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,

Java并发编程——线程池

一.Java中的ThreadPoolExecutor类 二.深入剖析线程池实现原理 三.使用示例 四.如何合理配置线程池的大小 一.Java中的ThreadPoolExecutor类 java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类.下面我们来看一下ThreadPoolExecutor类的具体实现源码. 在ThreadPoolExecutor类中提供了四个构造方法: public c

JAVA 并发编程-线程池(七)

线程池的作用: 线程池作用就是限制系统中运行线程的数量. 依据系统的环境情况.能够自己主动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其它线程排队等候.一个任务运行完毕,再从队列的中取最前面的任务開始运行. 为什么要用线程池: 1.降低了创建和销毁线程的次数,每一个工作线程都能够被反复利用.可运行多个任务. 2.能够依据系统的承受能力,调整线程池中工作线线程的数目,防止由于消耗过多的内存,而把server累趴下 Java里面线程池的

java并发编程-线程池的使用

参考文章:http://www.cnblogs.com/dolphin0520/p/3932921.html 深入剖析线程池实现原理 将从下面几个方面讲解: 1.线程池状态 2.任务的执行 3.线程池中的线程初始化 4.任务缓存队列及排队策略 5.任务拒绝策略 6.线程池的关闭 7.线程池容量的动态调整 1.线程池状态 在ThreadPoolExcutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态: volatile int runState

17.并发编程--线程池

并发编程线程池 合理利用线程池能够带来三个好处. 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行. 第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.但是要做到合理的利用线程池,必须对其原理了如指掌. 1. Executor 框架简介 在 Java 5 之后,并发编程引入了一堆新的启动.调度和管理 线

java并发编程线程安全

编写线程安全的代码实质就是管理对状态的访问,而且通常是共享的.可变的状态,对象的状态就是数据,存储在状态变量中,比如实例域,或者静态域,同时还包含了其它附属的域,例如hashmap的状态一部分存储到对象本身中,但同时也存储到很多mqp.entry中对象中,一个对象的状态还包含了任何会对他外部可见行为产生影响的数据. 所谓共享是指一个对象可以被多个线程访问, 所谓可变:是指变量的值在其生命周期内可以改变, 真正目的:在不可控制的并发访问中保护数据 线程安全必要条件: 1:对象是否被两个或以上的线程

JAVA 并发编程-线程同步通信技术(Lock和Condition)(十)

在之前的博客中已经介绍过线程同步通信技术<JAVA 并发编程-传统线程同步通信技术(四)>,上篇是使用的synchronized,wait,notify来实现,今天我们使用的是Lock和Condition,下面我们结合两者对比来学习. 简单的Lock锁应用: /** * 简单Lock的应用 * @author hejingyuan * */ public class LockTest { public static void main(String[] args) { new LockTest

java并发学习--线程池(一)

关于java中的线程池,我一开始觉得就是为了避免频繁的创建和销毁线程吧,先创建一定量的线程,然后再进行复用.但是要具体说一下如何做到的,自己又说不出一个一二三来了,这大概就是自己的学习习惯流于表面,不经常深入的结果吧.所以这里决定系统的学习一下线程池的相关知识. 自己稍微总结了一下,学习一些新的知识或者技术的时候,大概都可以分为这么几个点: 1.为什么会有这项技术,用原来的方法有什么问题. 2.这项新技术具体是怎么解决这个问题的(这时可能就要涉及到一些具体的知识点和编码了) 3.是不是使用这项技

java之并发编程线程池的学习

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类. corePoolSize在很多地方被翻译成核心池大小,其实我的理解这个就是线程池的大小.举个简单的例子: 假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务. 因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做: