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

JAVA线程池的实现与简单应用,第1张

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内置线程池代码实现

见下一篇博客

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/740612.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存