一、场景:
在了解了线程状态的转换,以及java提供的线程调度的一些方法之后,我尝试写一个线程池。
二、线程池的优缺点
(1)
谈到高并发必然离不开线程池,因为他有如下的优点:
1.1、降低资源的消耗。通过利用已创建的线程,来降低创建和销毁带了的消耗
1.2、当任务到达时候,不需要创建线程就可以立即执行。
1.3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。
三、如何编写线程池
先贴出代码
线程池主要代码
import java.util.LinkedList; import java.util.List;public class NewThreadPool {
volatile int counter =0;
private static int WORKNUM = 5;
List<Runnable> taskQueue = new LinkedList<Runnable>();// 已处理的任务 private static volatile int finished_task = 0; private WorkThread[] workThreads; private static NewThreadPool newThreadPool; private NewThreadPool(int workNum) { NewThreadPool.WORKNUM = workNum; // 定义一个工作线程数组 workThreads = new WorkThread[workNum]; for (int i = 0; i < workThreads.length; i++) { workThreads[i] = new WorkThread(); workThreads[i].start(); } } // 单例模式获得默认线程个数的线程池 public static NewThreadPool getThreadPool() { return getNewThreadPool(NewThreadPool.WORKNUM); } // 获得传入参数个数的线程池,大小不能小于默认个数 public static NewThreadPool getNewThreadPool(int work_num) { if (work_num <= 0) { work_num = NewThreadPool.WORKNUM; } if (null == newThreadPool) { newThreadPool = new NewThreadPool(work_num); } return newThreadPool; } //执行线程 public void execute(Runnable task){ synchronized (taskQueue) { taskQueue.add(task); taskQueue.notify(); } } //批量执行线程任务 public void execute(Runnable[] task){ synchronized (taskQueue) { for(Runnable t : task ){ taskQueue.add(t); taskQueue.notify(); } } } public void execute(List<Runnable>task){ synchronized (taskQueue) { for(Runnable t : task){ taskQueue.add(t); taskQueue.notify(); } } } //返回工作线程的个数 public int getWorkedThreadNumbers(){ return WORKNUM; } //返回已经完成的线程任务 public int getFinishedTaskNumbers(){ return finished_task; } //返回还没有完成任务的的线程个数 public int getWaitTaskNumbers(){ return taskQueue.size(); } //销毁线程池方法,等到所有线程都完成之后才会销毁 public void destory(){ while(!taskQueue.isEmpty()){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<WORKNUM;i++){ workThreads[i].stopWorker(); workThreads[i] = null; } newThreadPool = null; taskQueue.clear(); } // 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数 @Override public String toString() { return "WorkThread number:" + WORKNUM + " finished task number:" + finished_task + " wait task number:" + getWaitTaskNumbers(); } // 工作线程 private class WorkThread extends Thread { private boolean isRunning = true; @Override public void run() { Runnable r = null; while (isRunning) { synchronized (taskQueue) { while (isRunning && taskQueue.isEmpty()) { try { taskQueue.wait(20); } catch (InterruptedException e) { e.printStackTrace(); } } // 由于是队列,取第0个 if (!taskQueue.isEmpty()) { r = taskQueue.remove(0); } } if (null != r) { r.run(); //stopWorker(); } System.out.println(Thread.currentThread().getName() + "------------" + counter++ +"-----" + getWaitTaskNumbers()); finished_task++; r = null; } } // 停止工作,让该线程自然执行完run方法,自然结束 public void stopWorker() { isRunning = false; } }
}
以及测试demo
//测试线程池 public class TestThreadPool { public static void main(String[] args) { // 创建3个线程的线程池 NewThreadPool t = NewThreadPool.getNewThreadPool(10); t.execute(new Runnable[] { new Task(), new Task(), new Task() , new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task()}); t.execute(new Runnable[] { new Task(), new Task(), new Task() , new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task()}); System.out.println(t); //t.destory();// 所有线程都执行完成才destory System.out.println(t); t.execute(new Runnable[] { new Task(), new Task(), new Task() , new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task(), new Task()});} // 任务类 static class Task implements Runnable { private static volatile int i = 1; @Override public void run() {// 执行任务 System.out.println("任务 " + (i++) + " 完成"); } }
}
以上为线程池代码。参看了很多网友的博客,然后才理解了这个线程池的代码,然后按着理解写出了这个。如果发现有和网友代码有类似,不要惊奇,我是初学者请见谅。呵呵
(三)总结:
在编写线程池的时候,我们需要有一个基本的思路。需要一个基本的思路,一个安全的线程队列用来存放线程。按照要求初始化默认数量的线程,并且启动它,让这些线程在等待队列中等待,然后当目标任务来时,将其唤醒获得锁然后处理目标任务。
由于队列是不安全的,所以在线程添加到队列,和从队列中取出来的时候,都需要同步synchroniza,保证其安全。还有一个概念就是一旦线程池中run方法跑完,那么它的生命周期也就结束了。线程状态转换,参考 线程基础(详细介绍线程状态转换) ,所以再我们需要一个信号量,将工作线程的run方法中加上信号量,这样就可以保证,线程池中线程可以重用,只有当你销毁线程池的时候,所有线程才会销毁。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于