一、场景:
在了解了线程状态的转换,以及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方法中加上信号量,这样就可以保证,线程池中线程可以重用,只有当你销毁线程池的时候,所有线程才会销毁。