多线程

您所在的位置:网站首页 java多线程处理队列 多线程

多线程

2024-07-05 21:49:04| 来源: 网络整理| 查看: 265

多线程-- 线程池使用之等待所有任务执行完和关闭线程池 【一】自定义线程池【二】java自带的线程池【三】如何优雅的等待线程池所有任务执行完【四】如何优雅的关闭线程池【五】案例一:用线程池异步查询订单和收货地址(1)使用两个不同的线程分别查询订单和收货地址(2)使用线程池改造(3)使用线程池改造 【六】案例二:线程池模拟批量导入数据

【一】自定义线程池

(1)为什么使用线程池 每一个线程的启动和结束都是比较消耗时间和占用资源的。使用线程池的过程中创建固定数量的线程,不用创建多余新的线程,而是循环使用那些已经存在的线程。

(2)自定义线程池设计思路 1-准备一个任务容器 2-一次性启动10个消费者线程 3-刚开始任务容器是空的,所以线程都wait在上面 4-直到一个外部线程往这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒 5-这个消费者线程取出“任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来 6-如果短时间内,有较多的任务加入,name就会有多个线程被唤醒,去执行这些任务

public class ThreadPool { // 线程池大小 int threadPoolSize; // 任务容器 LinkedList tasks = new LinkedList(); // 试图消费任务的线程 public ThreadPool() { threadPoolSize = 10; // 启动10个任务消费者线程 synchronized (tasks) { for (int i = 0; i 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(); } } } } public class TestThread { public static void main(String[] args) { ThreadPool pool = new ThreadPool(); for (int i = 0; i @Override public void run() { //System.out.println("执行任务"); //任务可能是打印一句话 //可能是访问文件 //可能是做排序 } }; pool.add(task); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 【二】java自带的线程池

(1)线程池使用实例–使用java线程池实现穷举法破解密码

主要方法类

public class PassWordThreadpool { //第一步:把穷举法生成密码并且进行匹配的方法写好 //如果匹配到密码了就停止遍历 private boolean found = false; public synchronized void generatePwd(char[] guessPwd,String pwd, List pwdList) { generatePwd(guessPwd,0,pwd, pwdList); } public synchronized void generatePwd(char[] guessPwd,int index,String pwd, List pwdList) { //遍历数值和字母来生成密码 if(found){ return; } for (short i='0';i continue; } char c = (char) i; guessPwd[index] = c; if(index==pwd.length()-1){ //把三个字母的数组拼接成字符串 String guessResult = new String(guessPwd); pwdList.add(guessResult); if(guessResult.equals(pwd)){ System.out.println("密码找到了,是:"+guessResult); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间是:"+df.format(new Date())); found = true; return; } } else { generatePwd(guessPwd,index+1,pwd, pwdList); } } } }

线程池使用

public class TestThreadpoolGuesspwd { public static void main(String[] args) throws InterruptedException { String pwd = randomPwd(3); System.out.println("生成的密码是:"+pwd); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("当前时间是:"+df.format(new Date())); List pwdList = new CopyOnWriteArrayList(); PassWordThreadpool passWordThread = new PassWordThreadpool(); // 创建线程池 ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue()); // 把任务提交到线程池 threadPool.execute(() -> { char[] guessPwd = new char[pwd.length()]; passWordThread.generatePwd(guessPwd,pwd, pwdList); }); } public static String randomPwd(int length) { String pool = ""; for (short i = '0'; i pool += (char) i; } for (short i = 'A'; i int index = (int) (Math.random() * pool.length()); cs[i] = pool.charAt(index); } String result = new String(cs); return result; } } 【三】如何优雅的等待线程池所有任务执行完

(1)案例一:主线程在子线程结束前先结束了

public class TestThreadpoolImport01 { private static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,25,100L, TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadPoolExecutor.CallerRunsPolicy()); public static void main(String[] args) { Student student = null; List studentList = new ArrayList(); int age = 0; int heigh = 0; //添加50万个数据 for (int i=0;i //模拟对数据信息进行二次处理 executor.submit(()->student1.setName(student1.getName()+"这是后缀")); } long end = System.currentTimeMillis(); System.out.println("添加数量:"+studentList.stream().filter(x->x.getName().contains("这是后缀")).count()); System.out.println("花费时间:"+(end-start)); //输出的数据是 // 添加数量:371014 // 花费时间:114 //不是50w,这是由于线程池中的子线程任务没有执行完,而主线程已经开始执行业务代码,导致成功数量变少。 } }

以前在没有使用线程池之前,可以将所有线程放进线程数组,然后遍历数组,给每个线程对象调用join方法,现在来探索线程池的如何实现

(2)案例二:使用CountDownLatch

public class TestThreadpoolImport02 { private static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,25,100L, TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadPoolExecutor.CallerRunsPolicy()); public static void main(String[] args) { Student student = null; List studentList = new ArrayList(); int age = 0; int heigh = 0; //添加50万个数据 for (int i=0;i //模拟对数据信息进行二次处理 executor.submit(()->{ try { student1.setName(student1.getName()+"这是后缀"); } catch (Exception e) { e.printStackTrace(); } finally { //每执行一次数值减少一 countDownLatch.countDown(); //也可以给await()设置超时时间,如果超过300s(也可以是时,分)则不再等待,直接执行下面代码。 //countDownLatch.await(300,TimeUnit.SECONDS); } }); } try { //等待计数器归零 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("添加数量:"+studentList.stream().filter(x->x.getName().contains("这是后缀")).count()); System.out.println("花费时间:"+(end-start)); //输出的数据是 // 添加数量:500000 // 花费时间:334 //是50w,主线程等待所有子线程执行结束后才结束 } }

这是一个计数器操作,在线程池执行之前,给计数器指定数值(与要执行代码的次数一致)也就是students.size(),在线程池执行代码体里面要加上countDownLatch.countDown();代表每执行一次数值减少一,最后在循环体外边写上countDownLatch.await();代表等待计数器归零。

也可以给await()设置超时时间。如果超过300s(也可以是时,分)则不再等待,直接执行下面代码。

countDownLatch.await(300,TimeUnit.SECONDS);

(3)使用Future.get()

public class TestThreadpoolImport03 { private static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,25,100L, TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadPoolExecutor.CallerRunsPolicy()); public static void main(String[] args) { Student student = null; List studentList = new ArrayList(); int age = 0; int heigh = 0; //添加50万个数据 for (int i=0;i //使用submit提交会有一个返回值 Future future = executor.submit(()->{ try { //模拟对数据信息进行二次处理 student1.setName(student1.getName()+"这是后缀"); } catch (Exception e) { e.printStackTrace(); } }); futureList.add(future); } for (Future future:futureList) { try { //监听线程池子线程执行状态及执行结果。 future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } long end = System.currentTimeMillis(); System.out.println("添加数量:"+studentList.stream().filter(x->x.getName().contains("这是后缀")).count()); System.out.println("花费时间:"+(end-start)); //输出的数据是 // 添加数量:500000 // 花费时间:334 //是50w,主线程等待所有子线程执行结束后才结束 } }

使用submit提交线程,会返回一个Future的值,将每个返回值放进List,然后遍历调用get方法:future.get();

(4)使用shutdown方法 如果线程池是方法内部创建的,可以直接使用shutdown()也会等待线程池的执行结果。同时会关闭线程池资源。

public class TestThreadpoolImport04 { public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor(0,25,100L, TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadPoolExecutor.CallerRunsPolicy()); Student student = null; List studentList = new ArrayList(); int age = 0; int heigh = 0; //添加50万个数据 for (int i=0;i //模拟对数据信息进行二次处理 executor.submit(()->student1.setName(student1.getName()+"这是后缀")); } executor.shutdown(); try { executor.awaitTermination(300,TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("添加数量:"+studentList.stream().filter(x->x.getName().contains("这是后缀")).count()); System.out.println("花费时间:"+(end-start)); //输出的数据是 // 添加数量:500000 // 花费时间:281 //不是50w,这是由于线程池中的子线程任务没有执行完,而主线程已经开始执行业务代码,导致成功数量变少。 } } 【四】如何优雅的关闭线程池 【五】案例一:用线程池异步查询订单和收货地址 (1)使用两个不同的线程分别查询订单和收货地址 /** * @ClassName: TestThreadSearch * @Author: AllenSun * @Date: 2022/3/21 下午10:57 */ public class TestThreadSearch { public static void getOrder() { try { Thread.sleep(1000); System.out.println("获取订单信息"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void getAddress() { try { Thread.sleep(1000); System.out.println("获取地址信息"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { long start = System.currentTimeMillis(); getOrder(); getAddress(); long end = System.currentTimeMillis(); //花费时间:2007 System.out.println("花费时间:"+(end-start)); } }

耗时结果为

获取订单信息 获取地址信息 花费时间:2008 Process finished with exit code 0 (2)使用线程池改造 /** * @ClassName: TestThreadSearch * @Author: AllenSun * @Date: 2022/3/21 下午10:57 */ public class TestThreadPoolSearch { public static void getOrder() { try { Thread.sleep(1000); System.out.println("获取订单信息"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void getAddress() { try { Thread.sleep(1000); System.out.println("获取地址信息"); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadPoolExecutor executor= new ThreadPoolExecutor( 5, 10, 15, TimeUnit.SECONDS, new LinkedBlockingQueue() ); long start = System.currentTimeMillis(); //异步执行 CompletableFuture addressFuture = CompletableFuture.runAsync(()->{ getAddress(); },executor); //异步执行 CompletableFuture orderFuture = CompletableFuture.runAsync(()->{ getOrder(); },executor); //等待完成 CompletableFuture.allOf(addressFuture,orderFuture).get(); long end = System.currentTimeMillis(); //花费时间:1053 System.out.println("花费时间:"+(end-start)); executor.shutdown(); } }

耗时结果为

获取地址信息 获取订单信息 花费时间:1127 Process finished with exit code 0 (3)使用线程池改造 /** * @ClassName: TestThreadSearch * @Author: AllenSun * @Date: 2022/3/21 下午10:57 */ public class TestThreadPoolSearch2 { private static final CountDownLatch ctl = new CountDownLatch(2); public static void getOrder() { try { Thread.sleep(1000); System.out.println("获取订单信息"); ctl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void getAddress() { try { Thread.sleep(1000); System.out.println("获取地址信息"); ctl.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws ExecutionException, InterruptedException { ThreadPoolExecutor executor= new ThreadPoolExecutor( 5, 10, 15, TimeUnit.SECONDS, new LinkedBlockingQueue() ); long start = System.currentTimeMillis(); //异步执行 executor.execute(() -> { getOrder(); }); executor.execute(() -> { getAddress(); }); //等待完成 try { //等待计数器归零 ctl.await(20, TimeUnit.SECONDS);//最多等待20秒,不管子线程完没完 } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); //花费时间:1053 System.out.println("花费时间:"+(end-start)); executor.shutdown(); } }

耗时结果为

获取地址信息 获取订单信息 花费时间:1100 Process finished with exit code 0 【六】案例二:线程池模拟批量导入数据 public class TestThreadpoolImport02 { private static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,25,100L, TimeUnit.SECONDS,new LinkedBlockingQueue(),new ThreadPoolExecutor.CallerRunsPolicy()); public static void main(String[] args) { Student student = null; List studentList = new ArrayList(); int age = 0; int heigh = 0; //添加50万个数据 for (int i=0;i //模拟对数据信息进行二次处理 executor.submit(()->{ try { student1.setName(student1.getName()+"这是后缀"); } catch (Exception e) { e.printStackTrace(); } finally { //每执行一次数值减少一 countDownLatch.countDown(); //也可以给await()设置超时时间,如果超过300s(也可以是时,分)则不再等待,直接执行下面代码。 //countDownLatch.await(300,TimeUnit.SECONDS); } }); } try { //等待计数器归零 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("添加数量:"+studentList.stream().filter(x->x.getName().contains("这是后缀")).count()); System.out.println("花费时间:"+(end-start)); //输出的数据是 // 添加数量:500000 // 花费时间:334 //是50w,主线程等待所有子线程执行结束后才结束 } }


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭