Java简单实现线程池
本文实例为大家分享了Java简单实现线程池的具体代码,供大家参考,具体内容如下
一、线程池
线程池是一种缓冲提高效率的技术。
相当于一个池子,里面存放大量已经创建好的线程,当有一个任务需要处理时, 可以直接从池子里面取一个线程去执行它。 包括内存池,很多缓冲的技术都是采用这种技术。 其实理解起来很简答!
为什么需要线程池,这种池的技术?
1.1 减少开辟资源和销毁资源带来的损耗。
开辟线程,申请内存(具体的可以看C语言中malloc底层实现原理),销毁线程、释放内存资源等一些操作都是有时间消耗的。
因此一开始开辟大量的资源进行管理,需要使用时从池中取一个去使用, 使用完毕后再放回池中管理, 这样可以避免资源开辟和销毁带来的时间损耗。
1.2 提高响应。
用户来了一个请求, 能够立刻从开辟好的线程池中取一个线程去处理执行。 提高响应效率,提高用户体验。
1.3 有效管理资源
管理资源统一开辟和销毁, 监控线程状态和调优
二、线程池分析
对于线程池的实现我们划分为2个部分
1、线程安全的任务队列(采用队列,不过是线程安全的而已),保证工作线程在去任务时不会发生冲突(重复取同一个任务处理,二次执行或者多次的问题)。
2、对工作线程的监管(采用是List管理工作线程),方便线程的销毁和管理。
线程池处理逻辑:
1、每当添加一个任务,就会从线程池中取一个工作线程去处理执行它。
2、没有任务处理时, 工作线程应该处于阻塞状态等待任务到来, 不会竞争占用CPU资源
3、线程池相当于生产-消费模型, 只不过生产线程的中生产任务不同罢了。
3、主线程相当于监管线程,最终负责工作线程的销毁。
三、线程池实现
1、 工作线程Worker
1.1、工作线程负责从阻塞任务队列中取出任务执行。由于存在很多个线程对同一个队列操作,因此这个任务队列一定得是线程安全的(采用BlockingQueue接口, 这是GUC提供的,线程安全)
1.2、工作线程的创建方式属于线程创建的方式之一。
1.3、每个工作线程都维护一个阻塞任务队列。
1.4、线程的执行方法run()中,以线程的中断状态为循环判断条件(方便线程销毁, 只要将工作线程的中断状态置为true即可释放工作线程)其次就是BlockingQueue接口提供的take()方法。
该方法在队列没有元素时处于阻塞状态,直接取到元素,这样就解决了没有任务工作线程处于阻塞状态,不会抢占CPU
//实现工作线程 - 工作线程中维护了公有的任务队列(阻塞), 工作线程的执行逻辑。 循环取队列中的任务去执行处理。
class Worker extends Thread {
//阻塞任务队列 - 可以保证多个线程对队列操作, 线程安全
private BlockingQueue<Runnable> queue = null;
//每个工作线程都会有一个阻塞队列,这个队列中保存了所有的任务
public Worker(BlockingQueue<Runnable> queue, int id) {
this.queue = queue;
// Thread.currentThread().setName("郝梦武" + id + "号工作线程");
}
//工作线程执行内容
@Override
public void run() {
//每个线程通过isInterrupted()判断线程异常状态。
try {
while (!Thread.currentThread().isInterrupted()) {
//如果线程正常, 返回false, 出现异常, 返回true, 该状态默认为false
Runnable command = queue.take(); //如果队列为空, take会让线程阻塞
System.out.println(Thread.currentThread().getName() + "正在处理任务" + command.hashCode());
command.run();
}
}
catch(InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中止了");
// e.printStackTrace(); //不需要抛出异常
}
}
}
2、线程池对象MyThreadPool
1、创建工作线程并管理,添加任务。
2、销毁所有工作线程
//线程池 - 维护很多个线程, 当来一个任务时, 从线程池中获取一个线程去处理执行。
//好处: 防止线程频繁开辟和销毁带来的性能损耗
class MyThreadPool {
//创建任务线程安全的队列, 保证多个线程对这个队列操作时是线程安全的
private BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
//线程管理列表 - 这个列表保存了所有线程对象的引用, 方便后续的管理
private List<Worker> Wokers = new ArrayList<>();
private final static int maxWorkerCount = 10; //线程池最大允许的个数
//execute方法
public void execute(Runnable command) throws InterruptedException {
if(Wokers.size() < maxWorkerCount) {
//创建一个新的工作线程
Worker worker = new Worker(queue, Wokers.size()); //创建工作线程
worker.start(); //创建的工程线程启动
Wokers.add(worker); //添加到管理列表中
}
queue.put(command); //添加任务到线程安全的队列中
}
//销毁所有线程 - 将每个线程中状态置为中断状态方法, 并且
public void shutDown() throws InterruptedException {
for(Worker worker : Wokers) {
worker.interrupt(); //将线程的状态置为中断, 调用isInterruptd()返回值为true
}
//并且让主线程join阻塞等待所有工作线程
for(Worker worker : Wokers) {
worker.join(); //join方法可以让调用的线程处于阻塞状态, 知道等待的线程结束完毕之后就会恢复
}
//执行到这块, 代表所有的线程销毁完毕
System.out.println("所有工作线程销毁完毕!");
}
}
3、测试代码
class MyRunnable implements Runnable {
private int num;
MyRunnable(int num) {
this.num = num;
}
@Override
public void run() {
System.out.println("正在执行任务: " + num);
}
}
public static void main(String[] args) throws InterruptedException {
MyThreadPool myThreadPool = new MyThreadPool();
for(int i = 0; i < 1000; i++) {
myThreadPool.execute(new MyRunnable(i + 1));
}
Thread.sleep(2000); //主线程休眠2s
myThreadPool.shutDown(); //销毁所有工作线程
System.out.println("线程池已经被销毁了");
}
4、测试结果
总结:
以上的代码只是简单模拟实现了线程池。
不仅仅是线程池,内容池,还有很多池的应用场景。
池的技术虽然能够起到快速响应的特点,但是还是存在问题。
第一点: 池需要在一开始创建很多资源, 这和我们机器内存大小有关系。
第二点: 池中的线程过多,但是任务过少,导致很多线程浪费掉, 因此池中开辟多大的资源需要根据实际情况而言。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341