一个优秀的软件不会随意的创建、销毁线程,因为创建和销毁线程需要耗费大量的CPU时间以及需要和内存做出大量的交互。因此JDK5提出了使用线程池,让程序员把更多的精力放在业务逻辑上面,弱化对线程的开闭管理。

  JDK提供了四种不同的线程池给程序员使用  

  首先使用线程池,需要用到ExecutorService接口,该接口有个抽象类AbstractExecutorService对其进行了实现,ThreadPoolExecutor进一步对抽象类进行了实现。最后JDK封装了一个Executor类对ThreadPoolExecutor进行实例化,因此通过Executor能够创建出具有如下四种特性的线程池

  1. 无下界线程池

ExecutorService threadPool= Executors.newCachedThreadPool( ); 当线程数不足时,线程池会动态增加线程进行后续的任务调度

  2. 固定线程数的线程池

ExecutorService threadPool= Executors.newFixedThreadPool(3); 创建具有固定线程数:3个的线程池

  3. 单线程线程池

   ExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( );创建单例线程池,确保线程池内恒定有1条线程在工作

  4. 调度线程池

     ScheduledExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( ); 创建一个定长线程池,定时或周期性地执行任务

 package com.scl.thread.threadPool;

 import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; public class ScheduledThreadPool
{
public static void main(String[] args)
{
// 创建定时调度线程池
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 定时执行Runnable
threadPool.scheduleAtFixedRate(new Runnable()
{ @Override
public void run()
{
System.out.println("do some big Scheduled");
}
}, 10, 3, TimeUnit.SECONDS);
}
}

定时调度线程池

使用线程池也比较简单,只要往线程池里面提交"任务"即可,一般使用对象.submit的方法进行,如下:

   ① threadPool.submit(Callable<T>task);

Callable跟Runnable接口一样在使用接口时会自动调用里面的call方法。与Runnable接口不一样的地方在于run方法不能返回内容,call方法含有返回参数,在外部可以通过Future 接口来接受线程返回的结果。如:Future<String> future = threadPool.submit(new MyTask( ));

② threadPool.submit(Runnable task);

由这个签名可以看出,同样地可以往线程池里面扔入Runnable接口实例。

  这如上面所说,线程池的使用就是减少程序员对线程的启动和关闭。把线程的创建及关闭交给线程池来完成。因此使用线程池来完成线程的经典问题:生产者-消费者模型。

  模型简介:该模型是一个典型的排队内容。模型分为三个主要角色:生产者、消费者、仓库。生产者和消费者共享仓库信息,①当仓库里面的产品满了,停止生产等待消费者去消费到足够的量再进行生产  ②当仓库里面的产品为空,等待生产者生产后再进行销售  ③仓库负责承载产品内容

  根据模型内容,做成了一个车位管理的例子。分为三个角色:持车人(CarOwner)、保安(Secure)、车库(CarPark)。不同的持车人把车驶入到车库,保安负责记录车辆离开车库的数量。车库容量固定,如果车库为空保安可以通知持车人把车驶入车库。如果车库满了,保安告知持车人等待另外的持车人把车驾驶出来。车库使用一个列表对入库的车信息进行记录。

  因此有如下类图:

  

 package com.scl.thread.threadPool.CarParkManager;

 public class CarOwner implements Runnable
{
private int driverInNum;
private CarPark carPark; public CarOwner(CarPark carPark)
{
this.carPark = carPark;
} @Override
public void run()
{
carPark.driverIn(driverInNum);
} public int getDriverInNum()
{
return driverInNum;
} public void setDriverInNum(int driverInNum)
{
this.driverInNum = driverInNum;
}
}

持车人 CarOwner

 package com.scl.thread.threadPool.CarParkManager;

 import java.util.ArrayList;
import java.util.List;
import java.util.UUID; public class CarPark
{
protected int MaxCarNum;
private List<Car> carList = new ArrayList<Car>(); public CarPark(int maxNum)
{
this.MaxCarNum = maxNum;
} public void driverIn(int num)
{
synchronized (carList)
{
while (num + carList.size() > MaxCarNum)
{
System.out.println(Thread.currentThread() + ":无法进库,目前车库数目:" + carList.size());
try
{
carList.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
for (int i = 0; i < num; i++)
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
String uuid = UUID.randomUUID().toString();
Car c = new Car(uuid, uuid + i);
carList.add(c);
}
System.out.println(Thread.currentThread() + ":已经驶入" + num + "辆,目前车库数目:" + carList.size());
carList.notify();
}
} public void driverOut(int num)
{
synchronized (carList)
{ while (carList.size() < num)
{
System.out.println(Thread.currentThread() + ":无法驶出" + num + "辆,目前车库数目:" + carList.size());
try
{
carList.wait();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
for (int i = num - 1; i >= 0; i--)
{
try
{
Thread.sleep(20);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
carList.remove(i);
}
System.out.println(Thread.currentThread() + ":已经驶出" + num + "辆,目前车库数目:" + carList.size());
carList.notify();
}
} public List<Car> getCarList()
{
return carList;
} public void setCarList(List<Car> carList)
{
this.carList = carList;
} }

停车场 CarPark

 package com.scl.thread.threadPool.CarParkManager;

 public class Car
{
private String id;
private String name; public Car(String id, String name)
{
this.id = id;
this.name = name;
} }

汽车信息类 Car

 package com.scl.thread.threadPool.CarParkManager;

 public class Secure implements Runnable
{
private int driverOutNum; private CarPark carPark; public Secure(CarPark carPark)
{
this.carPark = carPark;
} public int getDriverOutNum()
{
return driverOutNum;
} public void setDriverOutNum(int driverOutNum)
{
this.driverOutNum = driverOutNum;
} @Override
public void run()
{
carPark.driverOut(driverOutNum);
}
}

保安 Secute

 package com.scl.thread.threadPool.CarParkManager;

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Client
{ public static void main(String[] args)
{
// 设置车库最大值
CarPark carPark = new CarPark(5);
// 创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// 创建三个持车人
CarOwner cw1 = new CarOwner(carPark);
CarOwner cw2 = new CarOwner(carPark);
CarOwner cw3 = new CarOwner(carPark);
// 设置需要驶入的车辆数目
cw1.setDriverInNum(3);
cw2.setDriverInNum(3);
cw3.setDriverInNum(1); // 创建三个保安
Secure s1 = new Secure(carPark);
Secure s2 = new Secure(carPark);
Secure s3 = new Secure(carPark);
s1.setDriverOutNum(1);
s2.setDriverOutNum(2);
s3.setDriverOutNum(1); threadPool.submit(cw1);
threadPool.submit(cw2);
threadPool.submit(cw3);
threadPool.submit(s1);
threadPool.submit(s2);
threadPool.submit(s3); } }

客户端 Client

补充:很多时候我们都会写一些demo,然后用JUnit进行测试。多线程用JUnit来测试真的很好用,但是有个很不人性化的就是:线程池里面的任务都作为一个类似C#所说的工作者线程进行调用。即:当主线程运行完毕,线程池里面的东西还没执行完毕整个程序就退出了。让主线程去等待子线程执行,有几个方法:①用子线程调用join方法(线程池不适用,本来线程池就是要弱化对线程的操作)②在主线程内用Thread.sleep(10000); 然而我们没法知道线程池内的任务到底要多久,sleep的数值一直要设置很大 ③找个方法获取线程池是否把任务执行完毕。毫无疑问第三种方法是最佳的。

  获取任务是否完成,需要按照以下两个步骤执行。(假如有线程池threadPool)

  1. 停止往线程池提交任务。调用threadPool.shutdown( );

  2. 循环判断线程池是否空置。调用threadPool.isTerminated( );

即可在Junit测试方法内,在添加完所有任务后,加上如下代码:

         threadPool.shutdown();
while (true)
{
//如果任务已经执行完毕,则跳出循环
if (threadPool.isTerminated())
{
break;
}
//主线程等待
Thread.sleep(1000);
}
//彻底关闭线程池
threadPool.shutdownNow();

由上述代码可见,使用线程池和使用Thread类进行提交没有太大的差异。JDK5提供了一个阻塞队列的,能够更好地模拟生产者-消费者模型。后续再进行总结。

以上为线程池的总结内容,如有错漏烦请指出纠正。

Java多线程(四) 线程池的更多相关文章

  1. Java多线程与线程池技术

    一.序言 Java多线程编程线程池被广泛使用,甚至成为了标配. 线程池本质是池化技术的应用,和连接池类似,创建连接与关闭连接属于耗时操作,创建线程与销毁线程也属于重操作,为了提高效率,先提前创建好一批 ...

  2. Java 多线程:线程池

    Java 多线程:线程池 作者:Grey 原文地址: 博客园:Java 多线程:线程池 CSDN:Java 多线程:线程池 工作原理 线程池内部是通过队列结合线程实现的,当我们利用线程池执行任务时: ...

  3. java多线程、线程池及Spring配置线程池详解

    1.java中为什么要使用多线程使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源.2.java中简单的实现多线程的方式 继承Thread ...

  4. Java多线程之线程池详解

    前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...

  5. JAVA多线程(三) 线程池和锁的深度化

    github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...

  6. Java多线程和线程池

    转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...

  7. Java多线程-ThreadPool线程池(三)

    开完一趟车完整的过程是启动.行驶和停车,但老司机都知道,真正费油的不是行驶,而是长时间的怠速.频繁地踩刹车等动作.因为在速度切换的过程中,发送机要多做一些工作,当然就要多费一些油. 而一个Java线程 ...

  8. Java多线程-ThreadPool线程池-2(四)

    线程池是个神器,用得好会非常地方便.本来觉得线程池的构造器有些复杂,即使讲清楚了对今后的用处可能也不太大,因为有一些Java定义好的线程池可以直接使用.但是(凡事总有个但是),还是觉得讲一讲可能跟有助 ...

  9. Java多线程之线程池

    现在是多核的时代,面向多核的编程很重要,因此基于java的并发和多线程开发非常重要. 线程池是于队列密切相关的,其中队列保存了所有等待执行的任务.工作者线程的任务很简单:从队列中获取一个任务,执行任务 ...

随机推荐

  1. mysql 优化工具

    explain  profiling 建议提供以下信息 show table status like 'audit';show create table audit;show index from a ...

  2. 小白日记22:kali渗透测试之提权(二)--抓包嗅探

    抓包嗅探 通过抓包嗅探目标机器的流量,发现账号密码. Windows系统 1.Wirehshark 2.Omnipeek 3.commview 4.Sniffpass 只会抓取识别传输密码的明文协议, ...

  3. hdu 2852 树状数组

    思路:加一个数e就用update(e,1).删除元素e就用update(e,-1).找比a大的第k大的元素就用二分查找. #include<iostream> #include<cs ...

  4. iOS开发者如何提高自己的水平(转)

    阅读. 把一大堆的知识塞进脑子里.随着时间流逝,终归有一些会留在脑海里.我觉得有些东西读起来还挺有意思,那么也能算作一种愉快的消遣. 分析. 多去熟悉并了解一些工具,从高层的到底层的,不要害怕去使用他 ...

  5. centos 安装 acrobat Reader之后

    IV: 为Firefox等浏览器安装Acrobat Reader插件:sudo /usr/local/Adobe/Acrobat7.0/Browser/install_browser_plugin按照 ...

  6. http状态代码含义表

    100 - 表示已收到请求的一部分,正在继续发送余下部分. 101 - 切换协议. 2xx - 成功.服务器成功地接受了客户端请求: 200 - 确定.客户端请求已成功. 201 - 已创建. 202 ...

  7. C# 实现远程控制软件的关键技术

    一.服务器端多线程Socket技术    用TcpListener进行侦听,接受客户端连接,有客户端连进来后开启处理线程处理数据,代码如下:    using System; using System ...

  8. 约瑟夫环(N个人围桌,C语言,数据结构)

    约瑟夫环问题(C语言.数据结构版) 一.问题描述 N个人围城一桌(首位相连),约定从1报数,报到数为k的人出局,然后下一位又从1开始报,以此类推.最后留下的人获胜.(有很多类似问题,如猴子选代王等等, ...

  9. 改变xmind显示中文界面

    最近打算迁移到ubuntu系统做为主系统,打算使用xmind这个工具,因为我的ubuntu并没有使用简体中文,xmind是根据系统的语种来自动选择的,所以也直接为英文界面,实在使用不习惯,查到了这篇文 ...

  10. 细说JavaScript单线程的一些事

    标签: JavaScript 单线程 首发地址:码农网<细说JavaScript单线程的一些事> 最近被同学问道 JavaScript 单线程的一些事,我竟回答不上.好吧,感觉自己的 Ja ...