how2j.cn


工具版本兼容问题
每一个线程的启动和结束都是比较消耗时间和占用资源的。

如果在系统中用到了很多的线程,大量的启动和结束动作会导致系统的性能变卡,响应变慢。

为了解决这个问题,引入线程池这种设计思想。

线程池的模式很像生产者消费者模式,消费的对象是一个一个的能够运行的任务


本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



30分57秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器



步骤 1 : 线程池设计思路   
步骤 2 : 开发一个自定义线程池   
步骤 3 : 测试线程池   
步骤 4 : 使用java自带线程池   
步骤 5 : 练习- 借助线程池同步查找文件内容   
步骤 6 : 答案- 借助线程池同步查找文件内容   

步骤 1 :

线程池设计思路

edit
线程池的思路和生产者消费者模型是很接近的。
1. 准备一个任务容器
2. 一次性启动10个 消费者线程
3. 刚开始任务容器是空的,所以线程都wait在上面。
4. 直到一个外部线程往这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒notify
5. 这个消费者线程取出“任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来。
6. 如果短时间内,有较多的任务加入,那么就会有多个线程被唤醒,去执行这些任务。

在整个过程中,都不需要创建新的线程,而是循环使用这些已经存在的线程
线程池设计思路
步骤 2 :

开发一个自定义线程池

edit
这是一个自定义的线程池,虽然不够完善和健壮,但是已经足以说明线程池的工作原理

缓慢的给这个线程池添加任务,会看到有多条线程来执行这些任务。
线程7执行完毕任务后,又回到池子里,下一次任务来的时候,线程7又来执行新的任务。
开发一个自定义线程池
package multiplethread; import java.util.LinkedList; public class ThreadPool { // 线程池大小 int threadPoolSize; // 任务容器 LinkedList<Runnable> tasks = new LinkedList<Runnable>(); // 试图消费任务的线程 public ThreadPool() { threadPoolSize = 10; // 启动10个任务消费者线程 synchronized (tasks) { for (int i = 0; i < threadPoolSize; i++) { new TaskConsumeThread("任务消费者线程 " + i).start(); } } } public void add(Runnable r) { synchronized (tasks) { tasks.add(r); // 唤醒等待的任务消费者线程 tasks.notifyAll(); } } class TaskConsumeThread extends Thread { public TaskConsumeThread(String name) { super(name); } Runnable task; public void run() { System.out.println("启动: " + this.getName()); while (true) { synchronized (tasks) { while (tasks.isEmpty()) { try { tasks.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } task = tasks.removeLast(); // 允许添加任务的线程可以继续添加任务 tasks.notifyAll(); } System.out.println(this.getName() + " 获取到任务,并执行"); task.run(); } } } }
package multiplethread; public class TestThread { public static void main(String[] args) { ThreadPool pool = new ThreadPool(); for (int i = 0; i < 5; i++) { Runnable task = new Runnable() { @Override public void run() { //System.out.println("执行任务"); //任务可能是打印一句话 //可能是访问文件 //可能是做排序 } }; pool.add(task); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
创造一个情景,每个任务执行的时间都是1秒
刚开始是间隔1秒钟向线程池中添加任务

然后间隔时间越来越短,执行任务的线程还没有来得及结束,新的任务又来了。
就会观察到线程池里的其他线程被唤醒来执行这些任务
测试线程池
package multiplethread; public class TestThread { public static void main(String[] args) { ThreadPool pool= new ThreadPool(); int sleep=1000; while(true){ pool.add(new Runnable(){ @Override public void run() { //System.out.println("执行任务"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); try { Thread.sleep(sleep); sleep = sleep>100?sleep-100:sleep; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
步骤 4 :

使用java自带线程池

edit
java提供自带的线程池,而不需要自己去开发一个自定义线程池了。

线程池类ThreadPoolExecutor在包java.util.concurrent


ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());


第一个参数10 表示这个线程池初始化了10个线程在里面工作
第二个参数15 表示如果10个线程不够用了,就会自动增加到最多15个线程
第三个参数60 结合第四个参数TimeUnit.SECONDS,表示经过60秒,多出来的线程还没有接到活儿,就会回收,最后保持池子里就10个
第四个参数TimeUnit.SECONDS 如上
第五个参数 new LinkedBlockingQueue() 用来放任务的集合

execute方法用于添加新的任务
package multiplethread; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class TestThread { public static void main(String[] args) throws InterruptedException { ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); threadPool.execute(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub System.out.println("任务1"); } }); } }
步骤 5 :

练习- 借助线程池同步查找文件内容

edit  姿势不对,事倍功半! 点击查看做练习的正确姿势
练习-同步查找文件内容 ,如果文件特别多,就会创建很多的线程。 改写这个练习,使用线程池的方式来完成。

初始化一个大小是10的线程池

遍历所有文件,当遍历到文件是.java的时候,创建一个查找文件的任务,把这个任务扔进线程池去执行,继续遍历下一个文件
步骤 6 :

答案- 借助线程池同步查找文件内容

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
答案- 借助线程池同步查找文件内容
package multiplethread; import java.io.File; import java.io.FileReader; import java.io.IOException; public class SearchFileTask implements Runnable{ private File file; private String search; public SearchFileTask(File file,String search) { this.file = file; this.search= search; } public void run(){ String fileContent = readFileConent(file); if(fileContent.contains(search)){ System.out.printf( "线程: %s 找到子目标字符串%s,在文件:%s%n",Thread.currentThread().getName(), search,file); } } public String readFileConent(File file){ try (FileReader fr = new FileReader(file)) { char[] all = new char[(int) file.length()]; fr.read(all); return new String(all); } catch (IOException e) { e.printStackTrace(); return null; } } }
package multiplethread; import java.util.LinkedList; public class ThreadPool { // 线程池大小 int threadPoolSize; // 任务容器 LinkedList<Runnable> tasks = new LinkedList<Runnable>(); // 试图消费任务的线程 public ThreadPool() { threadPoolSize = 10; // 启动10个任务消费者线程 synchronized (tasks) { for (int i = 0; i < threadPoolSize; i++) { new TaskConsumeThread("任务消费者线程 " + i).start(); } } } public void add(Runnable r) { synchronized (tasks) { tasks.add(r); // 唤醒等待的任务消费者线程 tasks.notifyAll(); } } class TaskConsumeThread extends Thread { public TaskConsumeThread(String name) { super(name); } Runnable task; public void run() { while (true) { synchronized (tasks) { while (tasks.isEmpty()) { try { tasks.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } task = tasks.removeLast(); // 允许添加任务的线程可以继续添加任务 tasks.notifyAll(); } task.run(); } } } }
package multiplethread; import java.io.File; public class TestThread { static ThreadPool pool= new ThreadPool(); public static void search(File file, String search) { if (file.isFile()) { if(file.getName().toLowerCase().endsWith(".java")){ SearchFileTask task = new SearchFileTask(file, search); pool.add(task); } } if (file.isDirectory()) { File[] fs = file.listFiles(); for (File f : fs) { search(f, search); } } } public static void main(String[] args) { File folder =new File("e:\\project"); search(folder,"Magic"); } }


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2024-08-07 练习- 借助线程池同步查找文件内容
木宇




练习- 借助线程池同步查找文件内容
package com.cwt.study.java中级.s_20240807;

import java.io.*;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Thread1 {

    public static void searchFile(String dir, String suffixName, String searchContent, ThreadPoolExecutor threadPoolExecutor) {
        //查找.java结尾文件
        File dirFile = new File(dir);
        //得到一个File类型的数组
        File[] files = dirFile.listFiles();
        for (File s : files) {
            //判断是否是文件
            if (s.isFile()) {
                //文件后缀名
//                new aThread(dir, suffixName, searchContent, s).start();
                threadPoolExecutor.execute(() -> {
                    if (s.getName().contains(suffixName)) {
                        try {
                            //读取文件
                            FileReader fileReader = new FileReader(s);
                            BufferedReader bufferedReader = new BufferedReader(fileReader);
                            while (true) {
                                //写文件
                                String line = bufferedReader.readLine();
                                if (line == null) {
                                    break;
                                }
                                if (line.contains(searchContent)) {
                                    System.out.println("搜索内容: " + line + ",找到文件位置: " + s.getAbsolutePath());
                                    break;
                                }
                            }
                        } catch (FileNotFoundException e) {
                            throw new RuntimeException(e);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
            } else {
                //判断文件夹,递归
                searchFile(s.getAbsolutePath(), suffixName, searchContent, threadPoolExecutor);
            }
        }

    }

    public static void main(String[] args) {
        String suffixName = ".java";
        String searchContent = "class";
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 15, 60,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        searchFile("D:\\java-study", suffixName, searchContent, threadPoolExecutor);

    }
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2024-07-14 自定义线程池 MyThreadPoll
虚心求学




执行结果如下: 启动消费者线程1 启动消费者线程4 启动消费者线程3 启动消费者线程2 启动消费者线程5 启动消费者线程6 启动消费者线程7 启动消费者线程9 启动消费者线程8 启动消费者线程10 用户已经添加了新任务 消费者线程10 已经拿到任务并执行 启动消费者线程10 用户已经添加了新任务 消费者线程1 已经拿到任务并执行 启动消费者线程1 用户已经添加了新任务 消费者线程10 已经拿到任务并执行 启动消费者线程10 用户已经添加了新任务 消费者线程1 已经拿到任务并执行 启动消费者线程1 用户已经添加了新任务 消费者线程8 已经拿到任务并执行 启动消费者线程8 全部任务执行完毕
package testThread;

import java.util.LinkedList;

public class MyThreadPoll {
	private int pollSize = 10;
	final private LinkedList<Runnable> Tasks;

	public MyThreadPoll() {
		Tasks = new LinkedList<>();
		for (int i = 0; i < pollSize; i++) {
			consumerThread thread = new consumerThread("消费者线程"+(i+1));
			thread.start();
		}
	}

	public void add(Runnable task) {
		synchronized (Tasks) {
			Tasks.add(task);
			Tasks.notifyAll();
		}

	}

	public void consume() {

	}

	class consumerThread extends Thread {
		public consumerThread(String name) {
			super(name);
		}

		@Override
		public void run() {
			while(true)
			{
				synchronized (Tasks) {
					System.out.println("启动"+this.getName());
					while (Tasks.isEmpty()) {
						try {
							Tasks.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					Runnable task = Tasks.removeLast();
					Tasks.notifyAll();
					task.run();
					System.out.println(this.getName() + " 已经拿到任务并执行");
				}
			}
			
		}
	}
}

————————主方法调用————————

public static void main(String[] args) {
		MyThreadPoll poll = new MyThreadPoll();
		for (int i = 0; i < 5; i++) {
			Runnable run = new Runnable() {
				
				@Override
				public void run() {
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};
			System.out.println("\t\t用户已经添加了新任务");
			poll.add(run);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("全部任务执行完毕");
	}

							


1 个答案

虚心求学
答案时间:2024-07-14
使用线程池来改写 “多线程查询是否包含 “magic" 内容的java文件” 的方法。 运行结果如下: 找到包含magic的子文件,文件名为:AP.java 找到包含magic的子文件,文件名为:APHero.java 找到包含magic的子文件,文件名为:ADAPHero.java 找到包含magic的子文件,文件名为:HelloWorld.java 找到包含magic的子文件,文件名为:Item.java



回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2022-11-03 答案
2022-08-04 查找文件
2022-06-22 答案


提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 61 条以前的提问,请 点击查看

提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-多线程-线程池 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 578362961
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图