线程池入门指南

简介: 线程池入门指南

线程池

参考:

基础概念

线程池:线程池就是创建大量空闲的线程并存入一个容器中;程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,任务执行完成后,该线程又返回线程池中成为空闲线程,等待执行下一个任务。

  • 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。少了浪费系统资源,多了造成系统拥挤效率不高。
  • 可以通过线程池控制线程数量,若待执行的任务数超过线程池的核心线程数,则排队等候。
  • 一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池就一直等待。
  • 当一个新任务传给线程池时,如果线程池中有等待的工作线程,就立刻使用等待中的工作线程执行该任务;否则任务进入等待队列。

线程池的特点:线程复用;控制最大并发数;管理线程

线程池的作用:避免频繁的创建线程和销毁线程,提高程序的效率

  • 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  • 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。


ThreadPoolExecutor(线程池)的执行顺序

  • 当线程数小于核心线程数时,会一直创建线程直到线程数等于核心线程数;
  • 当线程数等于核心线程数时,新加入的任务会被放到任务队列等待执行;
  • 当任务队列已满,又有新的任务时,会创建线程直到线程数量等于最大线程数;
  • 当线程数等于最大线程数,且任务队列已满时,新加入任务会被拒绝。


一个线程池包括以下四个基本组成部分:

  • 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
  • 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
  • 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
  • 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。


线程池接口的体系结构

20190728135341226.png

线程池的体系结构:

java.util.concurrent.Executor:负责线程的使用和调度的根接口

​ |--ExecutorService 子接口: 线程池的主要接口,提供了线程池生命周期方法

​ |--ThreadPoolExecutor: 线程池的实现类,提供了线程池的维护操作等相关方法

​ |--ScheduledThreadPoolExecutor : 继承 ThreadPoolExecutor,实现了 ScheduledExecutorService

​ 周期性任务调度的类实现


线程池的常用方法

ExecutorService:JDK提供的线程池

java.util.concurrent.Executor:描述线程池的顶级接口

唯一方法:

void execute(Runnable command)    // 提交一个 Runnable 任务用于执行


java.util.concurrent.ExecutorService:描述线程池的接口

常用方法:

Future<?> submit(Runnable task)        // 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
<T> Future<T> submit(Callable<T> task)    // 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
/*参数:
    Runnable task:传递Runnable接口的实现类对象(设置的线程任务) ==> 重写run方法,设置线程任务 ==> run方法没有返回值
    Callable<T> task:传递Callable接口的实现类对象(设置线程任务) ==> 重写call方法,设置线程任务 ==> call方法有返回值
返回值:
    Future:用来接收线程任务的返回值 ==> 用来接收call方法的返回值
    
注:Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,Callable会返回一个结果。
*/

java.util.concurrent.Future:表示异步计算的结果。用来接收 call 方法的返回值

接口中的方法:

V get()        // 方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
V get(long timeout, TimeUnit unit)        // 做多等待timeout的时间就会返回结果
    
boolean isDone()        // 判断当前方法是否完成
boolean isCancel()        // 判断当前方法是否取消

boolean cancel(boolean mayInterruptIfRunning)    // 停止一个任务
/*    如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,
    如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false
*/


submit 和 execute 区别:

方法名 返回值 任务接口 向外层调用者抛出异常
execute void Runnable 接口 无法抛出异常
submit Future Callable 接口和 Runnable 接口 能抛出异常,通过 Future.get 捕获抛出的异常
  • execute() 方法中的是 Runnable 接口的实现,所以只能使用 try-catch 来捕获 Checked Exception,通过实现UncaughtExceptionHande 接口处理 UncheckedException, 即 和普通线程的处理方式完全一致

    submit() 方法中抛出异常不管提交的是 Runnable 还是 Callable 类型的任务,如果不对返回值 Future 调用 get() 方法,都会吃掉异常

  • execute() 方法提交的未执行的任务可以通过 remove(Runnable) 方法删除

    submit() 方法提交的任务即使还未执行也不能通过 remove(Runnable) 方法删除


ExecutorService 接口的核心成员变量:

// 核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)
private volatile int corePoolSize;
// 线程池中当前的线程数
private volatile int poolSize;
// 线程池最大能容忍的线程数
private volatile int maximumPoolSize;


// 任务缓存队列,用来存放等待执行的任务
private final BlockingQueue<Runnable> workQueue;
// 线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
private final ReentrantLock mainLock = new ReentrantLock();
// 用来存放工作集
private final HashSet<Worker> workers = new HashSet<Worker>();  
// 线程存货时间
private volatile long keepAliveTime;       
// 是否允许为核心线程设置存活时间
private volatile boolean allowCoreThreadTimeOut;
// 任务拒绝策略
private volatile RejectedExecutionHandler handler; 
// 线程工厂,用来创建线程
private volatile ThreadFactory threadFactory;
// 用来记录线程池中曾经出现过的最大线程数
private int largestPoolSize;
// 用来记录已经执行完毕的任务个数
private long completedTaskCount;


java.util.concurrent.Executors:是一个创建线程池的工具类(工厂类),专门用来生产线程池,里边的方法都是静态的

常用方法:

// 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
static ExecutorService newFixedThreadPool(int nThreads)
/*参数:
    int nThreads:创建线程池,包含线程的数量  100==>线程池中包含100个线程
*/
static ExecutorService newCachedThreadPool()         // 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
static ExecutorService newSingleThreadExecutor()     // 创建单个线程池。 线程池中只有一个线程
static ScheduledExecutorService newScheduledThreadPool()     // 创建固定大小的线程,可以延迟或定时的执行任务

// 注意:线程池一旦销毁,就不能在使用了,会抛出异常
void shutdown()  // 关闭线程池,但不会立即终止,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
void shutdownNow()    // 立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务


使用示例

public class Test {
     public static void main(String[] args) {   
         ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                 new ArrayBlockingQueue<Runnable>(5));
          
         for(int i=0;i<15;i++){
             MyTask myTask = new MyTask(i);
             executor.execute(myTask);
             System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
             executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
         }
         executor.shutdown();
     }
}
class MyTask implements Runnable {
    private int taskNum;
     
    public MyTask(int num) {
        this.taskNum = num;
    }
     
    @Override
    public void run() {
        System.out.println("正在执行task "+taskNum);
        try {
            Thread.currentThread().sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task "+taskNum+"执行完毕");
    }
}


ThreadPoolTaskExecutor:Spring 提供的线程池

线程池对象交给 Spring 容器管理

  • 配置类的方式配置线程池

    @Configuration
    public class ExecturConfig {
        @Bean("threadPoolTaskExecutor")        // 配置多个线程池时,设置不同的实例名称,注入时变量名为相应的实例名称
        public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            int i = Runtime.getRuntime().availableProcessors();    //获取到服务器的cpu内核
            executor.setCorePoolSize(5);    //核心池大小
            executor.setMaxPoolSize(100);    //最大线程数
            executor.setQueueCapacity(1000);    //队列程度
            executor.setKeepAliveSeconds(1000);    //线程空闲时间
            executor.setThreadNamePrefix("tsak-asyn");    //线程前缀名称
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());    //配置拒绝策略
            executor.initialize();    //线程初始化
            return executor;
        }
  • xml 配置的方式创建

    <!-- spring线程池 -->
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 核心线程数  -->
        <property name="corePoolSize" value="10"/>
        <!-- 最大线程数 -->
        <property name="maxPoolSize" value="200"/>
        <!-- 队列最大长度 >=mainExecutor.maxSize -->
        <property name="queueCapacity" value="10"/>
        <!-- 线程池维护线程所允许的空闲时间 -->
        <property name="keepAliveSeconds" value="20"/>
        <!-- 线程池对拒绝任务(无线程可用)的处理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
        </property>
    </bean>


使用时通过自动注入的方式获取线程池对象

// 方式1(推荐)
@AutoWired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;

// 方式2
@Resource(name="taskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;


打印线程池的实时参数

public static void printThreadPoolStatic(ThreadPoolTaskExecutor threadPoolTaskExecutor){
    log.info("-----------------------thread pool status start-----------------------");
    log.info("thread core pool size:{}", threadPoolTaskExecutor.getCorePoolSize());    // 核心线程数
    log.info("thread active count:{}", threadPoolTaskExecutor.getActiveCount());     // 正在工作的线程数
    log.info("thread pool max size:{}", threadPoolTaskExecutor.getMaxPoolSize());     // 最大线程数  
    log.info("thread pool size:{}", threadPoolTaskExecutor.getPoolSize());    // 线程池中当前的线程数
    log.info("thread pool wait task:{}", threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().size());                                                                             // 任务队列内等待执行的任务数
    log.info("-----------------------thread pool status end-----------------------");
}


线程池的主要参数

// ThreadPoolExecutor类中提供的构造方法
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
        BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
  • corePoolSize核心线程数,也可以理解为最小线程数

    ​ (1)核心线程会一直存在,即使没有任务执行

    ​ (2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数

    ​ (3)设置 allowCoreThreadTimeout=true(默认 false)时,核心线程会超时关闭

  • maxPoolSize(maximumPoolSize)最大线程数

    ​ (1)线程池里允许存在的最大线程数量

    ​ (2)当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务

    ​ (3)线程池里允许存在的最大线程数量。当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务

  • keepAliveTime线程空闲时间

    ​ (1)当线程空闲时间达到 keepAliveTime 时,线程会退出(关闭),直到线程数等于核心线程数

    ​ (2)如果设置了 allowCoreThreadTimeout=true,则线程会退出直到线程数等于零

  • (BlockingQueue)workQueue任务队列,用于传输和保存等待执行任务的阻塞队列。阻塞队列有以下几种选择:

    • LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为 Integer.MAX_VALUE
    • synchronousQueue:不保存提交的任务,而是将直接新建一个线程来执行新来的任务
    • ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列
  • threadFactory线程工厂,用于创建新线程。

    threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)

  • (RejectedExecutionHandler)handler线程饱和策略/任务拒绝处理器,当线程池和队列都满了,再加入线程会执行此策略

    • ThreadPoolExecutor.AbortPolicy
      简单粗暴,丢弃任务并抛出RejectedExecutionException异常,这也是默认的拒绝策略。

      即使是submit提交,也是使用 try-catch 捕捉 任务拒绝异常

      try{
          Future<T> future = threadPoolTaskExecutor.submit(() -> {任务});
          T t = future.get();
      } catch(TaskRejectedException e){
          e.printStackTrace();
      } catch(Exception e){
          e.printStackTrace();
      }
    • ThreadPoolExecutor.CallerRunsPolicy
      如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。
    • ThreadPoolExecutor.DiscardPolicy
      丢弃任务,但是不抛出异常
    • ThreadPoolExecutor.DiscardOldestPolicy
      抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。
  • unit参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

    TimeUnit.DAYS;            // 天
    TimeUnit.HOURS;           // 小时
    TimeUnit.MINUTES;         // 分钟
    TimeUnit.SECONDS;         // 秒
    TimeUnit.MILLISECONDS;    // 毫秒
    TimeUnit.MICROSECONDS;    // 微妙
    TimeUnit.NANOSECONDS;     // 纳秒


排队的三种通用策略:

  • 直接提交

    工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不阻塞它们。

    如果不存在可用于立即运行任务的工作线程时,试图把任务加入队列将失败,因此会构造一个新的工作线程。

    此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

    直接提交策略通常要求无界的 maximumPoolSizes(最大线程数)以避免拒绝新提交的任务。

  • 无界队列(队列大小超级大,例如 Integer.MAX_VALUE)

    使用无界队列(例如,使用默认值的 LinkedBlockingQueue),如果所有的工作线程都忙时,新任务将在队列中等待。

    因为队列超级大,很难队列满,线程池中的线程数一般不会超过 corePoolSize(maximumPoolSize 的值也就用不到了)。

    当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。

    例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

  • 有界队列

    当使用有界队列(如 ArrayBlockingQueue)以及有限的 maximumPoolSizes(最大线程数)时,有助于防止资源耗尽,但是性能调优可能会比较困难。

    • 使用大型队列和小型池

      可以最大限度地降低 CPU 使用率以及操作系统资源和上下文切换的开销,但是可能导致人为的降低吞吐量。

      如果任务执行过程中可能频繁阻塞(例如,I/O 性能瓶颈),则耗时可能大于节约的线程调度开销。

    • 使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。


常见线程池

常见线程池(实现 ExecutorService 的四种连接池,使用工厂类 Executors 的静态方法创建):

  • newFixedThreadPool:固定线程池

    核心线程数和最大线程数固定相等,空闲存活时间为 0 毫秒(说明此参数无意义),工作队列为最大为 Integer.MAX_VALUE 大小的阻塞队列。

    当执行任务时,如果线程都很忙,就会丢到工作队列等有空闲线程时再执行,队列满就执行默认的拒绝策略。

  • newCachedThreadPool:带缓冲线程池

    核心线程数为 0,最大线程数为 Integer.MAX_VALUE,超过 0 个的空闲线程在 60 秒后销毁,SynchronousQueue(直接提交的队列)

    每个新任务都会有线程来执行,如果线程池有可用线程则执行任务,没有的话就创建一个来执行,线程池中的线程数不确定,一般建议执行速度较快的线程,不然这个最大线程池边界过大容易造成内存溢出。

  • newSingleThreadExecutor:单线程线程池

    核心线程数和最大线程数均为1,空闲线程存活 0 毫秒

    每次只执行一个线程,多余的先存储到工作队列,一个一个执行,保证了线程的顺序执行。

    如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。

  • newScheduledThreadPool:调度线程池

    大小无限制的线程池,支持定时和周期性的执行线程

img


线程池的合理配置分析

如何配置线程池,可以从以下几个角度来进行分析:

  • 任务的性质:CPU 密集型任务,IO 密集型任务和混合型任务

    任务性质不同的任务可以用不同规模的线程池分开处理:

    • CPU 密集型任务:配置尽可能少的线程数量,如配置 Ncpu+1 个线程的线程池
    • IO 密集型任务:由于需要等待 IO 操作,线程并不是一直在执行任务,则配置尽可能多的线程,如 2*Ncpu
    • 混合型的任务:如果可以拆分,则将其拆分成一个 CPU 密集型任务和一个 IO 密集型任务

      只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率

      如果这两个任务执行时间相差太大,则没必要进行分解

    注:可以通过 Runtime.getRuntime().availableProcessors() 方法获得当前设备的CPU个数

  • 任务的优先级:高、中和低

    优先级不同的任务:可以使用优先级队列 PriorityBlockingQueue 来处理

    优先级队列可以让优先级高的任务先得到执行

    需要注意的是如果一直有优先级高的任务提交到队列里,那么优先级低的任务可能永远不能执行。

  • 任务的执行时间:长、中和短

    执行时间不同的任务:

    • 可以交给不同规模的线程池来处理
    • 也可以使用优先级队列,让执行时间短的任务先执行
  • 任务的依赖性:是否依赖其他系统资源,如数据库连接

    由于线程提交 SQL 后需要等待数据库返回结果,等待的时间越长,CPU 空闲时间就越长

    如果等待的时间较长,则线程数应该设置大一点,这样才能更好的利用 CPU

    • 建议使用有界队列,有界队列能增加系统的稳定性和预警能力,可以根据需要设置大一点,比如几千
    • 如果设置成无界队列,当数据库出现了问题,导致执行 SQL 变得非常缓慢,进而导致线程池里的工作线程全部阻塞住,任务积压在线程池里,线程池的队列就会越来越多,有可能会撑满内存,导致整个系统不可用。


线程池的监控

  • 可以通过线程池提供的参数进行监控

    线程池里有一些属性在监控线程池的时候可以使用

    • taskCount:线程池需要执行的任务数量。
    • completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于 taskCount。
    • largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
    • getPoolSize:线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不减。
    • getActiveCount:获取活动的线程数。
  • 可以通过扩展线程池进行监控

    通过继承线程池并重写线程池的 beforeExecute,afterExecute 和 terminated 方法

    可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。

    这几个方法在线程池里是空方法。如:

    protected void beforeExecute(Thread t, Runnable r) { }`


定时器

定时器:可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情

java.util.Timer 类:

  • 是一个描述定时器的类
  • 一种工具,线程用其安排以后在后台线程中执行的任务
  • 可安排任务执行一次,或者定期重复执行

构造方法:

public Timer()         // 创建一个新计时器

成员方法:

void schedule(TimerTask task, long delay) // 在指定的毫秒值之后,执行指定的任务,只会执行一次
     // 参数:
     //     task - 所要安排的任务。定时器到时间之后要执行的任务
     //     delay - 执行任务前的延迟时间,单位是毫秒。 多少毫秒之后开始执行TimerTask任务
void schedule(TimerTask task, long delay, long period)
    // 在指定的毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复执行定时任务
    // 参数:
    //     period - 执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
void schedule(TimerTask task, Date time) // 安排在指定的时间执行指定的任务,只会执行一次
    // 参数:
    //     time - 执行任务的时间。从什么日期开始执行任务  20020-07-06 15:25:13
void schedule(TimerTask task, Date firstTime, long period) // 安排指定的任务在指定的时间开始进行重复的固定延迟执行。

void cancel()     // 终止此计时器,丢弃所有当前已安排的任务。
    // 注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。

java.util.TimerTask类 implements Runnable接口:由 Timer 安排为一次执行或重复执行的任务

TimerTask 类是一个抽象类,无法直接创建

void run()        // 此计时器任务要执行的操作。重写run方法,设置线程任务
相关文章
|
21天前
|
安全 数据处理 开发者
Python中的多线程编程:从入门到精通
本文将深入探讨Python中的多线程编程,包括其基本原理、应用场景、实现方法以及常见问题和解决方案。通过本文的学习,读者将对Python多线程编程有一个全面的认识,能够在实际项目中灵活运用。
|
3天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
12天前
|
安全 Java 调度
Java中的多线程编程入门
【10月更文挑战第29天】在Java的世界中,多线程就像是一场精心编排的交响乐。每个线程都是乐团中的一个乐手,他们各自演奏着自己的部分,却又和谐地共同完成整场演出。本文将带你走进Java多线程的世界,让你从零基础到能够编写基本的多线程程序。
26 1
|
1月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
74 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
18天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
41 3
|
20天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
2月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
78 0
|
3月前
|
机器学习/深度学习 Java TensorFlow
深度学习中的图像识别:从理论到实践Java中的多线程编程入门指南
【8月更文挑战第29天】本文将深入探讨深度学习在图像识别领域的应用,从基础理论到实际应用案例,带领读者一步步理解如何利用深度学习技术进行图像识别。我们将通过一个简单的代码示例,展示如何使用Python和TensorFlow库实现一个基本的图像识别模型。无论你是初学者还是有一定经验的开发者,都能从中获得启发和学习。 【8月更文挑战第29天】在Java世界里,线程是程序执行的最小单元,而多线程则是提高程序效率和响应性的关键武器。本文将深入浅出地引导你理解Java多线程的核心概念、创建方法以及同步机制,帮助你解锁并发编程的大门。
|
5月前
|
Java 数据处理 调度
Java多线程编程入门指南
Java多线程编程入门指南
|
5月前
|
Java 开发者
告别单线程时代!Java 多线程入门:选继承 Thread 还是 Runnable?
【6月更文挑战第19天】在Java中,面对多任务需求时,开发者可以选择继承`Thread`或实现`Runnable`接口来创建线程。`Thread`继承直接但限制了单继承,而`Runnable`接口提供多实现的灵活性和资源共享。多线程能提升CPU利用率,适用于并发处理和提高响应速度,如在网络服务器中并发处理请求,增强程序性能。不论是选择哪种方式,都是迈向高效编程的重要一步。
41 2