java线程池的使用与实现简单的线程池
来源:互联网 发布:fzltchjw gb1 0 mac 编辑:程序博客网 时间:2024/05/01 23:14
一、线程池是什么?
线程池是一个对于多任务处理或多线程处理的管理方案。我们只需要将任务提交给线程池,就可以完成我们的任务。线程池内部使用了队列对我们的任务进行排队,然后当工作线程空闲时,就去队列取任务进行处理。这就避免了我们自己创建线程。要知道创建线程是个比较重的活,消耗的资源比较大,系统需要为每个线程分配独立的内存空间,需要记录他们的运行状态等等。。。。对于数量多而轻量的任务,为每个任务创建线程就得不偿失了。比如android中的图片加载和服务器的登陆。
二、线程池有什么用?
1.减少创建新线程的资源消耗,对线程进行复用。
2.根据需要有效控制线程数量,避免线程过多,炸了
三、怎么使用线程池?
这里只介绍使用工厂类进行创建线程池,最简单的方法,也是最常用的方法。
0.任务
我们先定义一个测试任务,后面用,任务需要实现Runnable接口。
public class TestTask implements Runnable{ private int taskid ; public TestTask(int id) { this.taskid = id; } @Override public void run() { System.out.println("正在执行任务->"+taskid); } }
1.CachedThreadPool
可缓存线程池,灵活回收空闲线程,线程数无限大。
那么有人问了,既然线程数无限大,那这和我们自己新线程有什么不同呢?
看到了没有,可“缓存”,意思就是它创建的线程不是马上回收的,它会等上一段时间(keepAliveTime),如果有新任务来了,就继续工作,那就重用线程了。
ExecutorService cachedService = Executors.newCachedThreadPool(); cachedService.execute(new TestTask(0)); cachedService.execute(new TestTask(1)); cachedService.execute(new TestTask(2));
2.SingleThreadExecutor
单线程化线程池,按照指定顺序(FIFO,LIFO)优先级执行。
处理线程数固定为1。
FIFO:先进先出;LIFO:后进先出
这里又有人问了,就一个线程,用处不大啊?
其实我也觉得,哈哈。但是也有它的用途的,由于它是按顺序处理任务的,因此就很好处理任务执行顺序问题,还有就是线程池本身的复用功能了,比如不能在主线程运行的任务,必须创建新线程,这是时候直接使用线程池就行了。
ExecutorService service = Executors.newSingleThreadExecutor();//-单一线程池,jdk 1.5 service.execute(new TestTask(0)); service.execute(new TestTask(1)); service.execute(new TestTask(2));
3.FixedThreadPool
定长线程池,超过最大并发数,则队列等待
可以自定义并发线程数,下面是根据cpu核心数来设定并发线程数,这样可以根据不同的机器启动不同数量的线程,最大化的利用机器性能。
//获取cpu核心数 int processors = Runtime.getRuntime().availableProcessors(); ExecutorService fixedervice = Executors.newFixedThreadPool(processors); fixedervice.execute(new TestTask(0)); fixedervice.execute(new TestTask(1)); fixedervice.execute(new TestTask(2));
4.ScheduledThreadPool
定长线程池,支持定时和周期性任务
可用于定时和周期性的任务。
//实现周期任务ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);// 定时任务 timer.scheduleAtFixedRate(new TestTask(0), 10, //开始时间 10, //周期 TimeUnit.SECONDS/*单位*/);
四、内部原理及实现
如果仅仅限于使用,上面的已经足够日常使用了。但是我们都是喜欢技术的人,希望专研的深层次一点,了解内部是怎么实现的。那么本节将实现一个简单的线程池供各位看看他的真面目。
本实现参考java原生的ThreadPoolExecutor类(也就是上面工厂生成的线程池需要的核心类)实现。有兴趣可以查看ThreadPoolExecutor的源码。看不懂的再看看我这简单的实现~(≧▽≦)/~。
1.我们的需求
java原生的线程池参数是很多的。我们只实现部分。我们就实现一个定长的不需要回收的线程池吧。
2.实现方法
工作线程使用hashset进行存储;任务使用队列进行存储,这里使用LinkedBlockingQueue线程安全的队列;线程池状态使用volatile变量(轻量级的同步变量),并用常量定义了四种状态。需要原子操作时使用事务锁ReentrantLock进行同步。
3.实现原理
先初始化一定的工作线程,并启动,将引用放到hashset中。工作线程是一个死循环,不断尝试去取任务队列的任务,取到就运行任务,取不到等待(Queue.task()),直到用户停止线程池。
4.实现代码
package com.thread;import java.util.HashSet;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.locks.ReentrantLock;/** * @author 作者 E-mail: yufemgg@gmail.com * @date 创建时间:2016-10-26 上午11:44:40 * @version 1.0 * @Description: */public class ThreadPool { //线程池的状态 volatile int runState;//volatile变量是一个轻量级的synchronized。 static final int RUNINNG = 0; static final int SHUTDOWN = 1; static final int STOP = 2; static final int TERMINATED = 3; private int poolSize; private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();//任务队列,线程安全的 private ReentrantLock lock = new ReentrantLock();//事务锁,用作同步 private HashSet<WorkThread> work = new HashSet<WorkThread>();//存放工作线程 public ThreadPool(int poolSize) { this.poolSize = poolSize; init(); runState = RUNINNG; } //提交任务 public boolean execute(Runnable task){ if (task == null) throw new NullPointerException(); if(runState!=RUNINNG){ return false;//线程池已经终止。 } return queue.add(task); } //初始化工作线程 private void init(){ for (int i = 0; i < poolSize; i++) { WorkThread w = new WorkThread(); work.add(w); w.start(); } log("线程池初始化完成"); } //修改线程状态为SHUTDOWN,此时不能再添加新任务,但已添加的任务会执行完。 public void shutdown(){ runState = SHUTDOWN; } //修改线程状态为SHUTDOWN,此时不能再添加新任务,并尝试停止运行任务,队列中的任务可能不会全部运行完。 public void shutdownNow(){ runState = STOP; } //工作线程 class WorkThread extends Thread{ boolean runing = true; @Override public void run() { while (runing) { Runnable task = null; task = getTask(); if(task!=null){ task.run(); log(this+"$"+task.toString()+"运行完成"); }else if(task==null&runState>=SHUTDOWN){ runing = false; lock.lock(); work.remove(this); if(work.size()==0){ runState = TERMINATED; log("线程池停止"); } lock.unlock(); } } } } //到任务队列取一个任务 private Runnable getTask(){ if(runState>SHUTDOWN){ return null; }else{ try { return queue.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block } } return null; } private static void log(String msg){ System.out.println(msg); } //test public static void main(String[] args) { ThreadPool pool = new ThreadPool(2); pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } log("正在运行A"); } }); pool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } log("正在运行B"); } }); pool.execute(new Runnable() { @Override public void run() { log("正在运行C"); } }); pool.shutdown();// pool.shutdownNow(); }}
附:知识点说明
- HashSet 节点不重复的集合。
LinkedBlockingQueue 线程安全的链表式队列。
- add() 是入队,队列满则抛出异常
- put() 是入队,队列满则等待
offer() 是入队,队列满则返回false
poll() 出队,没有元素之间返回null
- take() 出队,没有元素等待
- remove() 出队,没有元素抛异常。
ReentrantLock 事务锁,锁内部的代码只有获得锁的时候才能运行,运行完释放锁。保证了内部代码的原子操作。
- 转载请注明出处:http://blog.csdn.net/u013565368/article/details/52936446
参考文章
【1】http://www.cnblogs.com/dolphin0520/p/3932921.html
【2】http://www.oschina.net/question/565065_86540
【3】http://blog.csdn.net/hsuxu/article/details/8985931
【4】http://blog.csdn.net/z69183787/article/details/46986823
- java线程池的使用与实现简单的线程池
- java线程池的简单介绍与使用
- java线程池的简单实现
- java 简单线程池的实现代码
- java线程池的简单实现
- Java实现一个简单的线程池
- 一个简单的java线程池实现
- 简单的线程池实现 Java
- 简单线程池的实现--JAVA/Python
- java 线程池的简单使用
- JAVA线程池Executor的简单使用
- Java—线程池的简单使用
- java线程池的简单使用
- JAVA线程池ThreadPoolExecutor的简单使用
- 简单的线程池实现
- 线程池的简单实现
- 简单线程池的实现
- 线程池的简单实现
- 在满眼是“坑”的影视投资圈中,《湄公河行动》背后投资人是如何一击就中的?
- 魔门云获得百万级天使轮投资,由创客100创投基金领投
- Spring MVC注解汇总
- 数据库设计原则
- android 的四种启动模式
- java线程池的使用与实现简单的线程池
- 写作资源
- Hql的参数绑定
- JavaScriptCore(1)
- poj1726 Tango Tango Insurrection
- 荷兰国旗问题(改造快速排序)
- 求两个数a和b的最大公约数
- HelloCharts重新绘图时旧数据残留问题
- JavaScriptCore(2)