多线程-3
线程池
概念
线程池(Thread Pool)使用池化技术管理和使用线程的机制。通俗说,就是事先把一个或者多个线程放入池子中,当任务执行时,直接使用线程,用完后不是关闭线程,而是归还到池子中,方便复用。
好处
主要类和分类方式
主要类
Executors
是线程池的基础类,线程池用的接口或者类都是通过该类创建或者返回的。
工厂和工具方法Executor , ExecutorService , ScheduledExecutorService , ThreadFactory和Callable在此包中定义的类。 该类支持以下几种方法:
创建并返回一个ExecutorService设置的常用的配置设置的方法。
创建并返回一个ScheduledExecutorService的方法, 其中设置了常用的配置设置。
创建并返回“包装”ExecutorService的方法,通过使实现特定的方法无法访问来禁用重新配置。
创建并返回将新创建的线程设置为已知状态的ThreadFactory的方法。
创建并返回一个方法Callable出的其他闭包形式,这样他们就可以在需要的执行方法使用Callable 。
ExecutorService
一个Executor ,提供方法来管理终端和方法,可以产生Future为跟踪一个或多个异步任务执行。
一个ExecutorService可以关闭,这将导致它拒绝新的任务。 提供了两种不同的方法来关闭ExecutorService 。 shutdown()方法将允许先前提交的任务在终止之前执行,而shutdownNow()方法可以防止等待任务启动并尝试停止当前正在执行的任务。
分类方式
固定大小的线程池(FixedThreadPool)
简介
创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
应用场景
已知服务器性能,使用固定大小的线程池,防止服务器因为线程过多,压力大而宕机。
实战
package com.aaa.mt.demo1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @FileName: FixedThreadPoolDemo
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 9:37
* @Version: 1.0.0
*/
public class FixedThreadPoolDemo {
public static void main(String[] args) {
//使用Executors创建固定长度的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//同时启动100个线程。。。
for (int i = 0; i < 100; i++) {
//启动线程,执行业务
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
});
}
//executorService.submit();
//等线程业务执行完,再关闭
executorService.shutdown();
//立即关闭
//executorService.shutdownNow();
}
}
带缓冲的线程池(CachedThreadPool)
简介
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
应用场景
适合大量短时任务执行,否则一定会因为线程多,压力大让服务器宕机。
实战
package com.aaa.mt.demo1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @FileName: FixedThreadPoolDemo
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 9:37
* @Version: 1.0.0
*/
public class CachedThreadPoolDemo {
public static void main(String[] args) {
//使用Executors创建固定长度的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//同时启动100个线程。。。
for (int i = 0; i < 100; i++) {
//启动线程,执行业务
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
});
}
//executorService.submit();
//等线程业务执行完,再关闭
executorService.shutdown();
//立即关闭
//executorService.shutdownNow();
}
}
单线程执行器(SingleThreadExecutor)
简介
建一个使用从无界队列运行的单个工作线程的执行程序。
应用场景
适合任务有序执行的场景。
实战
package com.aaa.mt.demo1;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @FileName: FixedThreadPoolDemo
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 9:37
* @Version: 1.0.0
*/
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
//使用Executors创建固定长度的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//同时启动100个线程。。。
for (int i = 0; i < 100; i++) {
//启动线程,执行业务
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
});
}
//executorService.submit();
//等线程业务执行完,再关闭
executorService.shutdown();
//立即关闭
//executorService.shutdownNow();
}
}
带调度的线程池(ScheduledThreadPool)
简介
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
应用场景
适合线程延迟或者定期执行。
实战
package com.aaa.mt.demo1;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @FileName: FixedThreadPoolDemo
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 9:37
* @Version: 1.0.0
*/
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
//使用Executors创建固定长度的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//延迟执行
/*scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
},10, TimeUnit.SECONDS);*/
//延迟加定期执行 两次开始时间的间隔 如果业务执行时间小于间隔时间 按照开始时间进行间隔
// 如果业务执行时间打于间隔时间 按照业务执行时间进行间隔
/* scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("开始执行时间是----------------------:"+new Date());
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
try {
Thread.sleep(7000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("结束时间是:"+new Date());
}
},5,5,TimeUnit.SECONDS);*/
//延迟加定期执行 上一次结束时间到下一次开始时间,间隔5秒
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("开始执行时间是----------------------:"+new Date());
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("结束时间是:"+new Date());
}
},2,5,TimeUnit.SECONDS);
}
}
核心参数
准备工作
Executors.newFixedThreadPool(5)->new ThreadPoolExecutor->ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)->this()->ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
工作原理
拒绝策略
submit和execute区别?
execute(Runnable run) : 只能执行Runnable的子类, 没有返回值和异常处理
submit(Runnable run/Callable call) :技能执行Runnable的子类,还能执行Callable子类,有返回值和异常处理
实战
package com.aaa.mt.demo1;
import java.sql.SQLOutput;
import java.util.UUID;
import java.util.concurrent.*;
/**
* @FileName: FixedThreadPoolDemo
* @Description:Execute和Submit区别
* @Author: zhz
* @CreateTime: 2024/11/26 9:37
* @Version: 1.0.0
*/
public class ExecuteAndSubmitDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//使用Executors创建固定长度的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
//启动线程,执行业务
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
});
//启动线程,执行业务 放Callable直接报红
/*executorService.execute(new Callable<String>(){
@Override
public String call() throws Exception {
return null;
}
});*/
//submit执行Runnable的子类
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,在执行xxx任务!!!");
}
});
//submit执行Callable的子类
Future<String> resultFuture = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + "正在执行,在执行xxx任务!!!");
String randomStr = UUID.randomUUID().toString();
System.out.println("随机生成的字符串:" + randomStr);
int[] intArray = {1,2,3};
//System.out.println(intArray[3]);
return randomStr;
}
});
//获取返回值
String returnValue = resultFuture.get();
System.out.println("返回值为:"+returnValue);
//executorService.submit();
//等线程业务执行完,再关闭
executorService.shutdown();
//立即关闭
//executorService.shutdownNow();
}
}
多线程实战
需求
使用多线程测试全班的IP是否通畅,使用单线程和多线程两种方式来完成,观察使用时间。实战中知道多线程提高任务执行效率。
单线程
package com.aaa.mt.demo2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @FileName: SingleThreadPingIP
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 15:02
* @Version: 1.0.0
*/
public class SingleThreadPingIP {
/**
* 封装通用的方法
* @param ip
*/
public static void pingIP(String ip){
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
//获取Runtime类的对象
//每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。
Runtime runtime = Runtime.getRuntime();
//使用runtime对象执行cmd命令,并返回进程对象 进程对象包含输出流,输出流,错误流
Process process = runtime.exec("ping "+ip);
//使用process获取输入流
inputStream = process.getInputStream();
//把字节流转为缓存字符流,能提高速度,防止乱码
bufferedReader = new BufferedReader(
new InputStreamReader(inputStream,"GB2312"));
//定义空字符串接受值
String readMsg = null;
//定义标识符
boolean isTrue = false;
//循环读取 readMsg=bufferedReader.readLine() 读取一行并赋值给readMsg
while((readMsg=bufferedReader.readLine())!=null){
//System.out.println(readMsg);
//判断结果中是否含有TTL,如果含有说明IP通畅 否则不同
if(readMsg.contains("TTL")){
//System.out.println("172.16.2.75 通畅!");
isTrue=true;
break;
}
if(readMsg.contains("无法访问目标主机")){
//System.out.println("172.16.2.75 通畅!");
//isTrue=true;
break;
}
}
//判断标识符
if(isTrue){
System.out.println(ip+"通畅");
}else {
System.out.println(ip+"不通畅");
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
if(inputStream!=null){
inputStream.close();
}
if(bufferedReader!=null){
bufferedReader.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
//测试所有IP是否通畅
//获取当前时间到1970年1月1号0点的毫秒
long start = System.currentTimeMillis();
//循环
for (int i = 2; i <= 52; i++) {
pingIP("172.16.2."+i);
}
//获取当前时间到1970年1月1号0点的毫秒
long end = System.currentTimeMillis();
//执行时间为:
System.out.println("执行时间为:"+(end-start)/1000+"秒");
}
}
多线程(实现Runnable或者使用线程池)
package com.aaa.mt.demo2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @FileName: SingleThreadPingIP
* @Description:
* @Author: zhz
* @CreateTime: 2024/11/26 15:02
* @Version: 1.0.0
*/
public class MultiThreadPingIP implements Runnable{
//因为在run方法中想使用ip 定义属性,然后使用构造方法赋值
private String ip;
/**
* 构造方法赋值
* @param ip
*/
public MultiThreadPingIP(String ip) {
this.ip = ip;
}
@Override
public void run() {
pingIP(ip);
}
/**
* 封装通用的方法
* @param ip
*/
public static void pingIP(String ip){
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
//获取Runtime类的对象
//每个Java应用程序都有一个Runtime类的Runtime ,允许应用程序与运行应用程序的环境进行接口。
Runtime runtime = Runtime.getRuntime();
//使用runtime对象执行cmd命令,并返回进程对象 进程对象包含输出流,输出流,错误流
Process process = runtime.exec("ping "+ip);
//使用process获取输入流
inputStream = process.getInputStream();
//把字节流转为缓存字符流,能提高速度,防止乱码
bufferedReader = new BufferedReader(
new InputStreamReader(inputStream,"GB2312"));
//定义空字符串接受值
String readMsg = null;
//定义标识符
boolean isTrue = false;
//循环读取 readMsg=bufferedReader.readLine() 读取一行并赋值给readMsg
while((readMsg=bufferedReader.readLine())!=null){
//System.out.println(readMsg);
//判断结果中是否含有TTL,如果含有说明IP通畅 否则不同
if(readMsg.contains("TTL")){
//System.out.println("172.16.2.75 通畅!");
isTrue=true;
break;
}
if(readMsg.contains("无法访问目标主机")){
//System.out.println("172.16.2.75 通畅!");
//isTrue=true;
break;
}
}
//判断标识符
if(isTrue){
System.out.println(ip+"通畅");
}else {
System.out.println(ip+"不通畅");
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
if(inputStream!=null){
inputStream.close();
}
if(bufferedReader!=null){
bufferedReader.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {
//测试所有IP是否通畅
//获取当前时间到1970年1月1号0点的毫秒
//long start = System.currentTimeMillis();
//循环
/* for (int i = 2; i <= 52; i++) {
new Thread(new MultiThreadPingIP("172.16.2."+i)).start();
}*/
//获取当前时间到1970年1月1号0点的毫秒
//long end = System.currentTimeMillis();
//执行时间为:
// System.out.println("执行时间为:"+(end-start)/1000+"秒");
ExecutorService executorService = Executors.newFixedThreadPool(30);
for (int i = 2; i <= 32; i++) {
final int j = i;
executorService.execute(new Runnable() {
@Override
public void run() {
MultiThreadPingIP.pingIP("172.16.2."+j+",线程名字为:"+Thread.currentThread().getName());
}
});
}
executorService.shutdown();
}
}