最全面的JAVA多线程知识总结

小天天天天    Java    999+ 次    2023-07-26 17:35:52


背景:

        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

        

  1. 返回值: Callable接口的call()方法可以返回一个结果,而Runnable接口的run()方法没有返回值。

  2. 异常抛出: call()方法声明了可能抛出Exception异常,而run()方法没有声明抛出任何异常。

  3. 使用方式: 在多线程编程中,Runnable一般用于执行无返回值的任务,而Callable用于执行有返回值的任务

  4. 执行方式: 在Java中,Runnable通常通过Thread类的构造函数传递给线程对象,并通过start()方法来启动线程。

    Callable通常通过ExecutorServicesubmit()方法提交给线程池来执行,并通过Future对象获取返回结果。

总结:

        你只管努力,剩下的交给时间。加油,打工人!


如果你觉得本篇文章对您有帮助,请打赏作者

上一篇: Ajax上传文件/照片时报错TypeError :Illegal invocation

下一篇: 为什么 MySQL 不建议使用 NULL 作为列默认值

最新评论

暂无评论

热门文章

最新评论

网站数据

网站文章数:481

今日UV/PV/IP:11/11/11

昨日UV/PV/IP:13/13 /13

TOP