一,问题

在没有使用线程池的时候,每次需要一个线程都得手动new Thread()方式创建线程,用完了再销毁。

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务

在Java中可以通过线程池来达到这样的效果。

Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行

一、线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。

二、线程池的体系结构:

java.util.concurrent.Executor : 负责线程的使用与调度的根接口
 |--**ExecutorService 子接口: 线程池的主要接口
    |--ThreadPoolExecutor 线程池的实现类
   |--ScheduledExecutorService 子接口:负责线程的调度
      |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService(即实现了接口,又继承了ThreadPoolExecutor实现类)

三、工具类 : Executors

ExecutorService newFixedThreadPool() : 创建固定大小的线程池
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程 ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务(调度)

四:操作步骤:以newFixedThreadPool为例:

1,创建一个5个线程的线程池:

ExecutorService pool = Executors.newFixedThreadPool(5);

2,创建任务对象,任务对象可以是Runnable接口对象,也可以是Callable接口对象,这里以Runnable接口对象为例:

类定义:

class ThreadPoolDemo implements Runnable { public void run(){ do something}}

创建任务:

ThreadPoolDemo tpd = new ThreadPoolDemo();

2,向线程池中加任务:使用pool.submit(任务对象)添加任务。

线程池添加任务有好几种方式:

        submit(Callable<T> task)
submit(Runnable task, T result)
submit(Runnable task)

以最后一直为例:

pool.submit(tpd);

3,线程执行完,关闭线程:

线程关闭有两种方法:

第一种:shutdown(),这种方式会等线程池中线程执行完了就关闭线程池。

第二张:shutdownNow(),不管线程池里面任务有没有执行完全,都会立刻关闭线程池。

五,Submit:一共有三种构造方法:

1,参数是Callable对象,可以获取任务的返回结果

 <T> Future<T> submit(Callable<T> task);

2,参数是Runnable对象,有返回结果

 <T> Future<T> submit(Runnable task, T result);

3,参数也是Runnable对象

 Future<?> submit(Runnable task);

5.1   分析 submit(Runnable task, T result)

之前的章节知道,Callable是任务是可以有返回值的,而Runnable是没有返回值的。但是对于第二个构造方法,使用Runnable对象作为参数也能返回结果。

那么结果是如何返回的呢,又是返回什么结果呢?我跟了一下源码,源码关键代码如下:

有一个实现类AbstractExecutorService,里面有一个方法:

    public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}

跟踪:newTaskFor(task, result);

通过这个方法一直跟踪,发现最后调用的是Excutors里面一个方法:

    public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

可知最后在返回的结果是下面这个类返回的

new RunnableAdapter<T>(task, result)

类RunnableAdapter很简单:

    static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

通过这个类,我们可以大胆猜想到返回的结果是什么:

通过submit(Runnable task, T result)分别传入两个参数,一个是task,一个是result。

线程运行的时候会调用CALL方法,里面先通过task.run调用Runnable对象里面自定义的run方法,然后直接返回result。

可见整个线程执行,没有对result进行其他操作,只是传入对象引用,最后返回。

可见:唯一可以修改result值的地方,就是通过Ruannabe对象task的run方法里面执行,线程任务执行完了就返回值。

也就是result传入是哪个对象,最终Future对象的get()获取到的就是哪个对象,只不过值的改变是在Runnbale里面的run方法里面修改的

看下面这个例子感受一下:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class TestSubmit { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor();
Data data = new Data();
//通过Future对象接收执行结果
Future<Data> future = executor.submit(new Task(data), data);
System.out.println(future.get().getName());
}
}
class Data {
String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
} class Task implements Runnable {
Data data; public Task(Data data) {
this.data = data;
}
public void run() {
System.out.println("This is ThreadPoolExetor#submit(Runnable task, T result) method.");
data.setName("kevin");
}
}

执行结果:

This is ThreadPoolExetor#submit(Runnable task, T result) method.
kevin

分析执行过程:

先定义了一个Data实例data,然后分别把引用传给Task类里面的构造方法,和result参数。

线程执行的过程中,修改了data的值,线程执行完了,返回result的值,也就是data的值。通过future.get()获取该data修改后的值。

5.2  操作submit(Runnable task);

例子如下:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class TestThreadPool
{
public static void main(String[] args) throws Exception {
//1. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5); ThreadPoolDemo tpd = new ThreadPoolDemo();
//2. 为线程池中的线程分配任务
for (int i = 0; i < 10; i++)
{
pool.submit(tpd);
}
//3. 关闭线程池
pool.shutdown();
}
}
class ThreadPoolDemo implements Runnable{ private int i = 0; @Override
public void run() {
while(i <= 100){
System.out.println(Thread.currentThread().getName() + " : " + i++);
}
}
}

5.3 使用:submit(Callable<T> task)

因为使用Callable有返回结果,可以通过Future对象获取。

先以单个线程为例:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class TestThreadPool
{
public static void main(String[] args) throws Exception {
//1. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5); Future<Integer> future = pool.submit(new Callable<Integer>(){ @Override
public Integer call() throws Exception {
int sum = 0; for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
});
System.out.println(future.get()); pool.shutdown();
}
}

如果多线程呢:为了接收多线程的结果,使用一个List接收结果:

package com.atguigu.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future; public class TestThreadPool
{
public static void main(String[] args) throws Exception {
//1. 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//创建一个List对象接收结果
List<Future<Integer>> list = new ArrayList<>(); for (int i = 0; i < 10; i++) {
Future<Integer> future = pool.submit(new Callable<Integer>(){ @Override
public Integer call() throws Exception {
int sum = 0; for (int i = 0; i <= 100; i++) {
sum += i;
}
return sum;
}
});
//向list里面添加结果
list.add(future);
}
//关闭线程
pool.shutdown(); for (Future<Integer> future : list) {
System.out.println(future.get());
}
}
}

JUC-线程池的更多相关文章

  1. 【JUC】JUC线程池框架综述

    一.前言 在分析完了JUC的锁和集合框架后,下面进入JUC线程池框架的分析,下面给出JUC线程池的总体框架,之后再逐一进行分析. 二.JUC线程池框架图 说明:从上图可知,JUC线程池框架中的其他接口 ...

  2. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  3. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  4. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  5. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  8. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  9. Java - "JUC线程池" 架构

    Java多线程系列--“JUC线程池”01之 线程池架构 概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介 ...

  10. Java - "JUC线程池" ThreadPoolExecutor原理解析

    Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...

随机推荐

  1. 优化算法动画演示Alec Radford's animations for optimization algorithms

    Alec Radford has created some great animations comparing optimization algorithms SGD, Momentum, NAG, ...

  2. executing external native build for cmake

    进一步调试的方法: 在Android studio下方打开terminal,然后: gradlew build --stacktrace

  3. YAML文件简介

    编程免不了要写配置文件,怎么写配置也是一门学问. YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便. 本文介绍 YAML 的语法,以 JS-YAML 的实现为例.你可以去 ...

  4. APPLE框架之高效便捷的Repository解决方案

    原文地址:http://perfy315.iteye.com/blog/1460226 Spring Data JPA 转至:http://note.sdo.com/u/855924134/n/P15 ...

  5. 什么是 Event Loop?

    http://www.ruanyifeng.com/blog/2013/10/event_loop.html 什么是 Event Loop? 作者: 阮一峰 日期: 2013年10月21日 [2014 ...

  6. C#如何通过反射调用带有ref或者out的参数的方法

    写这篇博客,原起今天CyljXu问我一个问题:如何通过反射调用带有ref或者out的参数的方法?想着可能其他人也会遇到这个问题,权且记录下来,以备后行者搜索到. 这个在MSDN上有解释,参考 Meth ...

  7. C#中的集合(HashTable与Array类)【转】

    一.Array类 1.Array类的属性 序号 属性 & 描述 1 IsFixedSize 获取一个值,该值指示数组是否带有固定大小. 2 IsReadOnly 获取一个值,该值指示数组是否只 ...

  8. Add Binary Leetcode java

    题目: Given two binary strings, return their sum (also a binary string). For example, a = "11&quo ...

  9. 添加 Github follow、star按钮到网页

    怎么把github的star/fork/watch三个按钮弄到自己网站上? 就是这个按钮如何弄到我的网站里面来,是否有API呢?mdo/github-buttons · GitHub这个超级方便已经添 ...

  10. Java方法containsAll学习

    有时候我们需要判断B链表是不是A链表的子集,我们可以使用A.containsAll(B)来判断,当返回值是true的时候就表明B链表是A链表的子集,当返回值是false时候就表明B链表不是A链表的子集 ...