Java多线程线程池:提升应用性能的终极利器

简介: Java多线程线程池:提升应用性能的终极利器

前言


Java的多线程编程一直是程序员们的挑战之一,而线程池则是在这个领域中的一颗璀璨明珠。本文将深入研究Java线程池,解开其神秘面纱,探索其工作原理、优势和最佳实践。我们将带您进入多线程的奇妙世界,让您轻松掌握如何高效地管理和利用线程池,提升Java应用的性能和稳定性。




线程池


我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。


那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

在Java中可以通过线程池来达到这样的效果。


线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。


合理利用线程池能够带来三个好处:


  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。




线程池的使用


Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在java.util.concurrent.Executors线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors工程类来创建线程池对象。


Java类库提供了许多静态方法来创建一个线程池:


Executors类中创建线程池的方法如下:


a、newFixedThreadPool 创建一个固定长度的线程池,当到达线程最大数量时,线程池的规模将不再变化。


b、newCachedThreadPool 创建一个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量;线程池规模无限制。


c、newSingleThreadPoolExecutor 创建一个单线程的Executor,确保任务对了,串行执行


d、newScheduledThreadPool 创建一个固定长度的线程池,而且以延迟或者定时的方式来执行,类似Timer;

使用线程池中线程对象的步骤:


  1. 创建线程池对象。
  2. 创建Runnable接口子类对象。(task)
  3. 提交Runnable接口子类对象。(take task)

获取到了一个线程池ExecutorService 对象,定义了一个使用线程池对象的方法如下:

public Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行

Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。


  1. 关闭线程池(一般不做)。

示例:

import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了:"+Thread.currentThread().getName());
        System.out.println("教完后,教练回到了游泳池");
    }
}
public class ThreadPoolDemo {
    public static void main(String[] args) {
//        //创建一个包含固定数量的线程池对象
//        ExecutorService executorService = Executors.newFixedThreadPool(2);
//        //创建一个包含单条线程的线程池
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
//        //创建一个带缓冲区的线程池,会根据需求创建线程
//        ExecutorService executorService = Executors.newCachedThreadPool();
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        //创建Runnable实例对象
        MyThread r = new MyThread();
        //自己创建线程的方式
//        Thread t = new Thread(r);
//        t.start();
//        //从线程池中获取线程对象,然后调用MyThread的run方法
//        executorService.submit(r);
//        //再获取一个线程对象,
//        executorService.submit(r);
//        executorService.submit(r);
//        //注意:submit方法调用后,程序并不终止,因为线程次控制了线程的关闭
//        //使用完,又归还到了线程池中,
//
//        //关闭线程池
//        executorService.shutdown();
        for (int i = 0; i < 10; i++) {
            scheduledExecutorService.schedule(r,10, TimeUnit.SECONDS);//延迟10秒执行
        }
        scheduledExecutorService.shutdown();;//执行到此处并不会马上关闭连接池
//        while(!scheduledExecutorService.isTerminated()){
//
//        }
        System.out.println("Main Thread finished at"+new Date());
    }
}




Callable接口


一般情况下,使用Runnable接口、Thread实现的线程我们都是无法返回结果的。但是如果对一些场合需要线程返回的结果。就要使用用Callable、Future这几个类。Callable只能在ExecutorService的线程池中跑,但有返回结果,也可以通过返回的Future对象查询执行状态。Future 本身也是一种设计模式,它是用来取得异步任务的结果

看看其源码:

public interface Callable<V> {
    V call() throws Exception;
}


它只有一个call方法,并且有一个返回V,是泛型。可以认为这里返回V就是线程返回的结果。

ExecutorService接口:线程池执行调度框架

<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);


示例:

import java.util.Random;
import java.util.concurrent.*;
class HandleCallable implements Callable<Integer> {
    private String name;
    public HandleCallable(String name) {
        this.name = name;
    }
    @Override
    public Integer call() throws Exception {
        System.out.println("task"+ name + "开始进行计算");
        Thread.sleep(3000);
        int sum = new Random().nextInt(300);
        int result = 0;
        for (int i = 0; i < sum; i++)
            result += i;
        return result;
    }
}
public class FutureTest{
    public static void main(String[] args) {
        System.out.println("main Thread begin at:"+ System.nanoTime());
        //创建线程池对象
        ExecutorService executor = Executors.newCachedThreadPool();
        HandleCallable task1 = new HandleCallable("1");
        HandleCallable task2 = new HandleCallable("2");
        HandleCallable task3 = new HandleCallable("3");
        //执行
        Future<Integer> result1 = executor.submit(task1);
        Future<Integer> result2 = executor.submit(task2);
        Future<Integer> result3 = executor.submit(task3);
        executor.shutdown();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        //获取到返回的直接
        try {
            System.out.println("task1运行结果:"+result1.get());
            System.out.println("task2运行结果:"+result2.get());
            System.out.println("task3运行结果:"+result3.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("main Thread finish at:"+ System.nanoTime());
    }
}




最后


本期结束咱们下次再见👋~

🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

相关文章
|
2月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
117 0
|
2月前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
277 83
|
3月前
|
存储 SQL 安全
Java 无锁方式实现高性能线程实战操作指南
本文深入探讨了现代高并发Java应用中单例模式的实现方式,分析了传统单例(如DCL)的局限性,并提出了多种无锁实现方案。包括基于ThreadLocal的延迟初始化、VarHandle原子操作、Record不可变对象、响应式编程(Reactor)以及CDI依赖注入等实现方式。每种方案均附有代码示例及适用场景,同时通过JMH性能测试对比各实现的优劣。最后,结合实际案例设计了一个高性能配置中心,展示了无锁单例在实际开发中的应用。总结中提出根据场景选择合适的实现方式,并遵循现代单例设计原则以优化性能和安全性。文中还提供了代码获取链接,便于读者实践与学习。
90 0
|
2月前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
222 83
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
3月前
|
存储 Java
说一说 JAVA 内存模型与线程
我是小假 期待与你的下一次相遇 ~
|
3月前
|
移动开发 Java
说一说 Java 是如何实现线程间通信
我是小假 期待与你的下一次相遇 ~
|
3月前
|
Java 数据挖掘 调度
Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法
本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。
218 5