背景:
2023年经营惨淡,经历了裁员就业跳槽再就业,在找工作过程中对于知识的梳理和总结,本文总结JAVA多线程。
应用场景:
需要同时执行多个任务或处理大量并发请求时,
目前常用的场景有:
- Web服务器: 在Web服务器中,每个请求通常都是一个独立的任务,通过使用多线程可以同时处理多个请求,提高服务器的吞吐量和响应速度。
- 并行计算: 对于需要处理大量计算的任务,如图像处理、数据分析、科学计算等,可以使用多线程并行处理,充分利用多核处理器的性能优势,加快计算速度。
- 数据爬虫: 网络爬虫通常需要同时处理多个网页的下载和解析。使用多线程可以并发地下载和解析多个网页,提高爬取效率。
- 实时数据处理: 对于实时数据处理应用,如实时监控、实时日志处理等,可以使用多线程实时处理和响应事件。
- 游戏开发: 在游戏开发中,多线程可以用于实现游戏逻辑和渲染的并行处理,提高游戏性能和流畅度。
- 并发容器: Java提供了线程安全的并发容器,如ConcurrentHashMap、ConcurrentLinkedQueue等,适用于多线程环境下的高并发数据存储和访问。
- 异步编程: 使用多线程可以实现异步编程,将耗时的任务放到后台执行,保持界面的响应性,提高用户体验。
- 定时任务: 使用多线程可以实现定时任务的执行,定期执行一些需要周期性处理的任务。
- 线程池: 使用线程池可以有效地管理和复用线程资源,避免频繁创建和销毁线程,提高性能和资源利用率。
创建方法:
1、implements Runnable | extends Thread
2、ExecutorService实现类ThreadPoolExecutor
3、Executors.newFixedThreadPool
示例代码:
示例1:SimpleWebServer-模拟web服务器通过使用多线程可以同时处理多个请求
package xyz.lijiantao.study.thread;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Description 在Web服务器中,每个请求通常都是一个独立的任务,通过使用多线程可以同时处理多个请求,提高服务器的吞吐量和响应速度。
* @Date 2023/7/26 14:05
* @Created by LIJIANTAO
*/
public class SimpleWebServer {
public static void main(String[] args) {
final int port = 8080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Web server is listening on port " + port);
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("New client connected: " + clientSocket.getInetAddress());
// 创建新线程处理客户端请求
Thread requestHandlerThread = new Thread(new RequestHandler(clientSocket));
requestHandlerThread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
static class RequestHandler implements Runnable {
private final Socket clientSocket;
public RequestHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {
// 从客户端读取请求内容
String request = in.readLine();
System.out.println("Received request from client: " + request);
// 模拟处理请求的耗时任务
Thread.sleep(2000);
// 返回响应给客户端
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/html");
out.println();
out.println("<html><body>");
out.println("<h1>Hello, Web Server!</h1>");
out.println("</body></html>");
clientSocket.close();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
示例2:MonitorThreadDealEfficient-模拟多线程VS单线程处理任务效率
package xyz.lijiantao.study.thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description 模拟多线程处理任务
* @Date 2023/7/26 10:44
* @Created by LIJIANTAO
*/
public class MonitorThreadDealEfficient {
public static void main(String[] args) throws InterruptedException {
// 创建ThreadPoolExecutor线程池
int corePoolSize = 30; // 核心线程数,表示线程池中保持活动状态的线程数量
int maxPoolSize = 50; // 最大线程数,表示线程池允许创建的最大线程数量
long keepAliveTime = 5; // 线程空闲时间,超过该时间未执行任务的线程将被回收
TimeUnit timeUnit = TimeUnit.SECONDS; // 空闲时间单位
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize, maxPoolSize, keepAliveTime, timeUnit,
new LinkedBlockingQueue<>() // 任务队列,使用无界队列LinkedBlockingQueue
);
final CountDownLatch latch = new CountDownLatch(corePoolSize);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000 * 10; i++) {
Task task = new Task(i,"multiThread");
threadPoolExecutor.submit(task);
}
// 等待所有线程执行完成
while (true){
// 判断是否还有正在执行的任务
if (threadPoolExecutor.getActiveCount() > 0) {
// System.out.println("There are still active tasks.");
} else {
System.out.println("No active tasks.");
long end = System.currentTimeMillis();
System.out.println("multiThread 执行花费时间: " + (end - start) / 1000 + "s");
// 关闭线程池
threadPoolExecutor.shutdown();
break;
}
}
long start1 = System.currentTimeMillis();
for (int i = 0; i < 1000 * 10; i++) {
Task task1 = new Task(i);
task1.run();
}
long end1 = System.currentTimeMillis();
System.out.println("single thread 执行花费时间: " + (end1 - start1) / 1000 + "s");
}
static class Task implements Runnable{
private int executeNum;
private CountDownLatch latch;
private String executeType = "single thread";
public Task(int executeNum,CountDownLatch latch){
this.executeNum = executeNum;
this.latch = latch;
}
public Task(int executeNum){
this.executeNum = executeNum;
}
public Task(int executeNum,String executeType){
this.executeNum = executeNum;
this.executeType = executeType;
}
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
finally {
// System.out.println(executeType +" Task executed times: " + (executeNum + 1) + " is running on thread " + Thread.currentThread().getName());
}
}
}
}
示例3:ParallelProcessingExample-多线程并行处理示例代码
package xyz.lijiantao.study.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @Description java多线程并行处理示例代码
* @Date 2023/7/26 14:26
* @Created by LIJIANTAO
*/
public class ParallelProcessingExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int numThreads = 4; // 假设有4个线程并行处理任务
int numTasks = 10; // 假设有10个任务
// 创建线程池,用于并行处理任务
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
// 创建任务列表和结果列表
List<Callable<String>> tasks = new ArrayList<>();
List<Future<String>> results;
// 创建任务并添加到任务列表
for (int i = 0; i < numTasks; i++) {
final int taskId = i;
tasks.add(() -> {
// 模拟处理任务的耗时
Thread.sleep(1000);
return "Task " + taskId + " is completed by " + Thread.currentThread().getName();
});
}
// 并行处理任务并获取结果
results = executor.invokeAll(tasks);
// 关闭线程池
executor.shutdown();
// 打印任务执行结果
for (int i = 0; i < numTasks; i++) {
Future<String> result = results.get(i);
System.out.println(result.get());
}
}
}
常见的框架:
- Executor框架: Executor框架是Java标准库中提供的多线程框架,通过Executors工具类可以创建不同类型的线程池,如FixedThreadPool、CachedThreadPool、SingleThreadPool等。Executor框架提供了高级的任务执行和管理功能,适用于大部分简单的多线程任务。
- Fork/Join框架: Fork/Join框架是Java标准库中提供的用于并行计算的框架。它适用于大规模的任务划分和处理,并且可以利用多核处理器的优势来提高计算性能。
- Akka: Akka是一个开源的、基于Actor模型的并发框架。它提供了高级的并发抽象,可以让开发者更容易地编写并发、分布式和容错的应用程序。Akka是用Scala编写的,但也支持Java。
- RxJava: RxJava是ReactiveX的Java实现,它是一个异步编程库,支持响应式编程范式。RxJava可以帮助您处理异步任务、事件流和并发问题,提供了强大的工具来处理数据流和事件序列。
- Quasar: Quasar是一个基于Fiber(纤程)的并发框架,它可以让您使用更轻量级的线程(纤程)来实现并发任务。Quasar提供了对Java并发原语的增强,使得编写高效且易于维护的并发代码更加容易。
其他知识点:
1、ThreadPoolExecutor VS ThreadPool
在Java中都是用于管理线程池的类,但它们之间存在一些区别:
- 接口和实现:
ThreadPoolExecutor是Java标准库中的一个具体类,它实现了ExecutorService接口,用于管理线程池。ThreadPoolExecutor提供了丰富的线程池配置选项和管理方法,可以更加灵活地定制线程池的行为。
ThreadPool并不是Java标准库中的类,它可能是您自己实现的一个类或者某个第三方库提供的一个类。ThreadPool可能是基于ThreadPoolExecutor实现的,也可能是其他方式实现的。由于不是标准库中的类,ThreadPool可能不提供和ThreadPoolExecutor一样的配置选项和管理方法。
- 配置和灵活性:
ThreadPoolExecutor提供了丰富的配置选项,可以根据需要配置核心线程数、最大线程数、线程空闲时间、任务队列类型、拒绝策略等。这使得您可以根据具体应用场景和资源需求来定制线程池的行为。
ThreadPool可能只提供了一组简单的配置选项,可能不如ThreadPoolExecutor灵活,适用性可能相对较差。
- 可扩展性:
由于ThreadPoolExecutor是Java标准库的一部分,它是标准和通用的线程池实现。它可以直接在Java应用中使用,并且由于是标准库的一部分,未来的Java版本也会继续支持。
ThreadPool的实现可能是特定于某个库或项目的,如果使用第三方库提供的ThreadPool,则需要确保其可靠性和可维护性。
综上所述,ThreadPoolExecutor是Java标准库中用于管理线程池的标准实现,提供了丰富的配置选项和管理方法,可以在Java应用中直接使用。而ThreadPool可能是自定义的线程池实现或者某个第三方库提供的线程池实现,需要根据具体情况进行选择。一般情况下,优先考虑使用标准库中的ThreadPoolExecutor,以便获得更好的可扩展性和维护性。
2、Callable VS Runable
返回值:
Callable
接口的call()
方法可以返回一个结果,而Runnable
接口的run()
方法没有返回值。异常抛出:
call()
方法声明了可能抛出Exception
异常,而run()
方法没有声明抛出任何异常。使用方式: 在多线程编程中,
Runnable
一般用于执行无返回值的任务,而Callable
用于执行有返回值的任务执行方式: 在Java中,
Runnable
通常通过Thread
类的构造函数传递给线程对象,并通过start()
方法来启动线程。而
Callable
通常通过ExecutorService
的submit()
方法提交给线程池来执行,并通过Future
对象获取返回结果。
总结:
你只管努力,剩下的交给时间。加油,打工人!