JAVA线程池的实现与简单应用

JAVA线程池的实现与简单应用

一:概念简述

1.线程池是什么

  • List item
  • 线程池是一种多线程的处理机制,当任务添加到线程池当中的时候会被添加到任务队列当中,当线程创建并启动的时候,会自动的执行处理这些任务。这里的任务是指我们用runnable接口和callable接口实现的线程任务。

2.为啥使用线程池

  • 程序当中多个线程存在的时候不方便管理,即使任务闲置任务还在运行
  • 如果任务较多,闲置线程销毁,新的任务线程在重新生成的时间耗费较大
  • 线程池可以重复的利用线程,提高了效率和资源的合理利用性

3.线程池的优点

  • 线程和任务分离,提高线程的重复利用率
  • 控制线程并发数量;降低服务器的压力;统一管理所有的线程
  • 资源消耗减少(不用重复的消耗创建线程)
  • 响应速度加快(直接讲线程放入任务队列当中,加快了任务执行的效率)–假如创建线程速度t1,执行任务速度t2,销毁线程速度t3,利用线程池免去了t1和t3任务执行的时间
  • 系统更加稳定(线程是一种重要的资源,线程池可以统一的调度,监控,分配)

4.线程池的简单大致框架

在这里插入图片描述

二:自定义线程池的两种代码实现

1 第一种自定义线程池的实现方法

(1)ThreadPool线程池类的实现
import java.util.LinkedList;
import java.util.List;
public class ThreadPool {
	//保存任务的任务队列
	private List<Runnable> runnable =new LinkedList<>();
	//保存核心线程的线程队列
	private List<WorkThread> worklist =new LinkedList<>();
	public ThreadPool(int i)//输入参数代表创建几个核心的员工线程
	{
		for(int j=0;j<i;j++) 
		{
			//只需要将任务列表传递给工作类即可
			WorkThread wk =new WorkThread(runnable);
			wk.start();
			worklist.add(wk);
		}
	}
	//线程池还有就是将任务提交到任务列表的方法
	//为啥要使用线程安全相关的代码synchronized,因为当核心工作线程创建的时候,添加这个动作可能会被
	//多个线程执行,当多个动作对同一个操作对象进行操作的时候就要运用到线程安全相关的知识、多个动作操作一个对象(这个动作就要加上线程安全相关方法)
	public void addwork(Runnable r) 
	{	//无论是消费者还是生产者都只能顺序的一个一个拿到执行权
		//多个操作的对象,作为线程锁
		synchronized (runnable) 
		{
			runnable.add(r);
			//每次添加完毕以后高速,工作线程有任务了唤醒工作线程
			//释放锁的条件:当代码块中的代码执行完毕;遇到同步锁的wait方法;代码块当中遇到return返回的时候
			runnable.notify();
		}
	}
}

(2)WorkThread核心线程类的实现

import java.util.List;
//该类的作用是:执行任务队列当中的任务
public class WorkThread extends Thread{
	private List<Runnable> runnable;
	public WorkThread(List<Runnable> runnable) {
		// TODO Auto-generated constructor stub
		this.runnable =runnable;
	}
	//就是处理任务的代码块
	public void run() 
	{
		Runnable r=null;
		while(true) 
		{
			synchronized (runnable) {
				while(runnable.size()<=0) 
				{
					//当任务队列当中没有任务的时候,拿到锁的线程释放锁wait(),在线程等待的过程当中,有了任务以后,就会唤醒刚刚wait等待的锁继续执行
					try {
						runnable.wait();
						System.out.println("线程"+Thread.currentThread().getName()+"进入等待状态");
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				r=runnable.remove(0);
			}//锁的释放并不代表执行权的释放,锁释放以后还是继续执行r.run()操作
			//锁释放以后又会
			//执行任务
			r.run();
			System.out.println("线程"+Thread.currentThread().getName()+"执行完任务");
		}
	}

}
(3)Test类的实现
package com.lk.ThreadPool0423;

public class Test {
	public static void main(String[] args) {
		//测试只需要创建线程池和添加任务即可(上传任务)
		System.out.print("启动任务");
		ThreadPool tp=new ThreadPool(5);//创建了一个内核五个线程的线程池
		for(int i=0;i<20;i++) 
		{
			Work work =new Work(i);
			tp.addwork(work);
		}
	}
}
class Work implements Runnable
{
	int id;
	public Work(int i) 
	{
		this.id=i;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"正在执行任务  "+id);
		
	}
	
}
(4)执行效果展示

在这里插入图片描述

2 第二种自定义线程池的实现方法

(1)ThreadPool类的实现
package com.lk.PoolThread0421;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

//线程池需要有:线程数,核心线程数,最大线程数,队列
//功能有提交和执行的作用
public class ThreadPool {
	int num;
	int coresize;
	int maxsize;
	int listsize;
	public List<Runnable> lists=Collections.synchronizedList(new LinkedList<>());
	/**
	 * @param coresize
	 * @param maxsize
	 * @param listsize
	 * @param lists
	 */
	public ThreadPool(int coresize, int maxsize, int listsize) {
		super();
		this.coresize = coresize;
		this.maxsize = maxsize;
		this.listsize = listsize;
	}
	/**
	 * @param coresize
	 * @param maxsize
	 * @param lists
	 */
	public ThreadPool(int coresize, int maxsize, List<Runnable> lists) {
		super();
		this.coresize = coresize;
		this.maxsize = maxsize;
		this.lists = lists;
	}
	//将任务提交给任务队列,然后并执行该任务
	public void submit(Runnable r) 
	{
		if(lists.size()>listsize) 
		{
			System.out.println("任务"+r+"被丢弃了");
		}
		else 
		{
			lists.add(r);
			//执行任务
			exec(r);
		}
	}
	public void exec(Runnable r) 
	{
		if(num<coresize) 
		{
			new Workers("核心线程1", lists).start();
			num++;
		}else if(num<maxsize) 
		{
			new Workers("核心线程2",lists).start();
			num++;
		}
		else 
		{
			//因为r改了toString 里面的内容所以,r再加字符串的时候自动变化为字符
			System.out.println("任务"+r+"已经被缓存了");
		}
	}

}

(2)Tasks类的实现
package com.lk.PoolThread0421;
//任务参数id
//执行一个消耗大概两秒操作的任务
public class Tasks implements Runnable{
	int id;
	//快速的有参构造函数
	/**
	 * @param id
	 */
	public Tasks(int id) {
		super();
		this.id = id;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		//看看现在这个Runnable接口实现的线程依附于那个线程实现
		String name =Thread.currentThread().getName();
		System.out.println("线程"+name+"即将执行任务"+id);
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程"+name+"完成任务:"+id);
	}
	//当打印这个类对象的时候显示这个值
	public String toString()
	{
		return "{Task:"+id+"}";
	}
	
}

(3) Workers类的实现
package com.lk.PoolThread0421;

import java.util.List;

//类的作用,从队列当中取出数据运行操作
//这里用Thread的原因是runnable必须依附于thread执行,刚好和任务的线程接口对应
public class Workers extends Thread{
	//保存名字,比如核心线程与否
	public String name;
	public List<Runnable> tasks;
	/**
	 * @param name
	 * @param tasks
	 */
	public Workers(String name, List<Runnable> tasks) {
		super();
		this.name = name;
		this.tasks = tasks;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		//当队列当中有任务从队列当中取出来执行即可
		if(tasks.size()>0) 
		{
			Runnable r=tasks.remove(0);
			r.run();
		}
		
	}

}

(4).Test类的实现
package com.lk.PoolThread0421;

public class Test {
	public static void main(String[] args) {
		//
		ThreadPool tp=new ThreadPool(2,4,20);
		for(int i=0;i<30;i++) 
		{
			Tasks t=new Tasks(i);
			tp.submit(t);//任务上传给线程池
		}
	}

}
(5)运行效果展示

在这里插入图片描述

三:JAVA内置线程池参数的理解

观察ThreadPoolExecutor的源码来了解线程池的工作原理
在这里插入图片描述

-举一个例子来简单说明一下JAVA内置线程池参数的理解
假设线程池的核心线程数2最大线程数3任务队列是1饱和处理机制:是任务队列满了以后丢弃

  • a客户(任务)去银行(线程池)办理业务,银行还没开始营业,则代表线程池当中的线程的数量是零,这个时候银行管理者安排一个业务员去窗口上给a办理业务
  • b客户来到银行也做同样的处理(管理者再次安排一个核心业务员(核心线程)到窗口进行工作)
  • c客户到来的时候,因为a,b客户都在办理业务(无空闲核心线程),所以c在银行大厅(任务队列)坐着等待
  • d客户来到银行,这个时候无窗口(无核心线程),无空闲的座位(任务队列已满),银行管理者安排一个临时工业务员3站在大厅给d客户办理业务(最大线程数-核心线程数量)
  • e客户来到银行以后吗,啥都是满的,就按照饱和处理机制来处理e客户(不接待e客户)
  • 当临时工接待完d客户以后,等待的时间超过最大等待时间的时候,临时工下去了(销毁相关线程),但是正式的窗口的正式工要一直在哪里等着,等待新的客户(任务)的到来
  • 注意这里最大空闲时间:非核心线程空闲多少时间以后(单位由时间单位unit决定),会销毁非核心线程

四:JAVA内置线程池代码实现

见下一篇博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值