谈谈java的线程池(创建、机制)

目录

Executors创建线程池默认方法

自定义线程池


Executors创建线程池默认方法

    newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。

    newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列。

   newCachedThreadPool()方法,返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有空闲的线程则直接执行任务,若无空闲则创建线程,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收。

   newScheduledThreadPool()该方法返回一个ScheduledExecutorService对象,但该线程池可以指定线程的数量,可以定时。

前三种线程池添加线程:
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Thread());
//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
Future f1 = pool.submit(new Thread());
f1.get()//如果为空,则线程执行完毕了,否则会一直阻塞在这里。
//ScheduledThreadPool相当于定时任务
class Temp extends Thread {
    public void run() {
        System.out.println("run");
    }
}
public class ScheduledJob {
     
    public static void main(String args[]) throws Exception {
    
      Temp command = new Temp();
        ScheduledExecutorService scheduler =  Executors.newScheduledThreadPool(1);
        
        // 该方法,会在目标方法执行完之后,间隔1秒执行
        ScheduledFuture<?> scheduleTask =  scheduler.scheduleWithFixedDelay(command, 5, 1,  TimeUnit.SECONDS);//5秒初始化之后执行一次,以后每1秒执行一次

        // scheduleAtFixedRate ,会按照固定的频率执行,跟方法执行时间没关系
    
    }
}

其实底层都是new了ThreadPoolExecutor。

    若Executors工厂类无法满足我们的需求,可以自己去创建自定义的线程池,其实Executors工厂类里面的创建线程方法其内部实现均是用了ThreadPoolExecutor这个类,这个类可以自定义线程。构造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                       int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                  ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}
* corePoolSize: 线程池核心线程数(线程池初始化存在的线程)
* maximumPoolSize:线程池最大数
* keepAliveTime: 空闲线程存活时间
* unit: 时间单位
* workQueue: 线程池所使用的缓冲队列
* threadFactory:线程池创建线程使用的工厂
* handler: 线程池对拒绝任务的处理策略

自定义线程池

这个构造方法对于队列是什么类型的比较关键。

使用有界队列时:若有新的任务需要执行,如果线程池实际线程小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程去执行新任务,若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。

使用无界队列时:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

JDK拒绝策略

    AbortPolicy:直接抛出异常组织系统正常工作。

    CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。

    DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务。

    DiscardPolicy:丢弃无法处理的任务,不给予任何处理。

//如果需要自定义拒绝策略可以实现RejectedExecutionHandler接口。
public class MyTask implements Runnable {
     private int taskId;
     private String taskName;
     
     public MyTask(int taskId, String taskName){
           this.taskId = taskId;
           this.taskName = taskName;
     }
     
     public int getTaskId() {
           return taskId;
     }
     public void setTaskId(int taskId) {
           this.taskId = taskId;
     }
     public String getTaskName() {
           return taskName;
     }
     public void setTaskName(String taskName) {
           this.taskName = taskName;
     }
     @Override
     public void run() {
           try {
                System.out.println("run taskId =" +  this.taskId);
                Thread.sleep(5*1000);
                //System.out.println("end taskId =" +  this.taskId);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }          
     }
     
     public String toString(){
           return Integer.toString(this.taskId);
     }
}


public class UseThreadPoolExecutor1 {
     public static void main(String[] args) {
           /**
            * 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
            * 若大于corePoolSize,则会将任务加入队列,
            * 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
            * 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
            *
            */  
           ThreadPoolExecutor pool = new ThreadPoolExecutor(
                     1,                   //coreSize
                     2,                   //MaxSize
                     60,             //60
                     TimeUnit.SECONDS,
                     new ArrayBlockingQueue<Runnable>(3)             //指定一种队列 (有界队列)
                     //new LinkedBlockingQueue<Runnable>()
                     , new MyRejected()
                     //, new DiscardOldestPolicy()
                     );
           
           MyTask mt1 = new MyTask(1, "任务1");
           MyTask mt2 = new MyTask(2, "任务2");
           MyTask mt3 = new MyTask(3, "任务3");
           MyTask mt4 = new MyTask(4, "任务4");
           MyTask mt5 = new MyTask(5, "任务5");
           MyTask mt6 = new MyTask(6, "任务6");
           
           pool.execute(mt1);
           pool.execute(mt2);
           pool.execute(mt3);
           pool.execute(mt4);
           pool.execute(mt5);
           pool.execute(mt6);
           
           pool.shutdown();
           
     }
}

public class MyRejected implements RejectedExecutionHandler{
     
     public MyRejected(){
     }
     
     @Override
     public void rejectedExecution(Runnable r,  ThreadPoolExecutor executor) {
           System.out.println("自定义处理..");
           System.out.println("当前被拒绝任务为:" +  r.toString());
           
     }
}
public class UseThreadPoolExecutor2 implements Runnable{
     private static AtomicInteger count = new AtomicInteger(0);
     
     @Override
     public void run() {
           try {
                int temp = count.incrementAndGet();
                System.out.println("任务" + temp);
                Thread.sleep(2000);
           } catch (InterruptedException e) {
                e.printStackTrace();
           }
     }
     
     public static void main(String[] args) throws Exception{
           //System.out.println(Runtime.getRuntime().availableProcessors());
           BlockingQueue<Runnable> queue =
                     //new LinkedBlockingQueue<Runnable>();
                     new ArrayBlockingQueue<Runnable>(10);
           ExecutorService executor  = new ThreadPoolExecutor(
                           5,         //core
                           10,  //max无界队列的这个参数其实没啥作用了
                           120L,      //2fenzhong
                           TimeUnit.SECONDS,
                           queue);
           
           for(int i = 0 ; i < 20; i++){
                executor.execute(new UseThreadPoolExecutor2());
           }
           Thread.sleep(1000);
           System.out.println("queue size:" + queue.size());         //10
           Thread.sleep(2000);
     }
}

线程池的方法

向线程池提交任务

execute()

execute()方法用于提交不需要返回值的任务Runnable,所以无法判断任务是否被线程池执行成功。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.stream.IntStream;

@Slf4j
public class ExecuteDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        IntStream.rangeClosed(1, 3).forEach(i -> threadPoolExecutor.execute(() -> log.info("Task {} working...", i))); // 提交任务
        threadPoolExecutor.shutdown(); // 关闭线程池
    }
}

运行结果如下:

2020-10-13 14:58:09,500  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 1 working...
2020-10-13 14:58:09,502  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 2 working...
2020-10-13 14:58:09,502  INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 3 working...

submit()
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,get()方法也支持带超时时间的阻塞。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.stream.IntStream;

@Slf4j
public class SubmitDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        Future<String> future = threadPoolExecutor.submit(() -> {
            log.info("Task begin...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("Task end.");
            return "task result";
        });

        threadPoolExecutor.shutdown(); // 关闭线程池

        String result = future.get(); // 阻塞等待获取结果
        log.info("get result:{}", result);
    }
}

运行结果如下:

2020-10-13 15:03:22,306  INFO [pool-1-thread-1] (SubmitDemo.java:14) - Task begin...
2020-10-13 15:03:25,315  INFO [pool-1-thread-1] (SubmitDemo.java:20) - Task end.
2020-10-13 15:03:25,316  INFO [main] (SubmitDemo.java:27) - get result:task result

invokeAll()

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
public class InvokeAllDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> "task " + i).collect(Collectors.toList());

        List<Future<String>> futures = threadPoolExecutor.invokeAll(tasks);// 批量提交任务

        for (Future<String> future : futures) {
            log.info("result:{}", future.get()); // 阻塞获取结果
        }

        threadPoolExecutor.shutdown();
    }
}

运行结果如下:

2020-10-13 16:09:00,518  INFO [main] (InvokeAllDemo.java:25) - result:task 1
2020-10-13 16:09:00,520  INFO [main] (InvokeAllDemo.java:25) - result:task 2
2020-10-13 16:09:00,520  INFO [main] (InvokeAllDemo.java:25) - result:task 3

invokeAny()

invokeAny批量提交任务,哪个任务先执行完毕,立刻返回此任务执行结果,其它任务取消。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
public class InvokeAnyDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> {
            log.info("task{} begin ...", i);
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
            log.info("task{} end .", i);
            return "task" + i;
        }).collect(Collectors.toList());

        String result = threadPoolExecutor.invokeAny(tasks); // 哪个任务执行完就立马返回,其他任务直接中断

        log.info("result:{}", result); // 阻塞获取结果

        threadPoolExecutor.shutdown();

    }
}

运行结果如下:

2020-10-13 16:09:35,121  INFO [pool-1-thread-1] (InvokeAnyDemo.java:21) - task1 begin ...
2020-10-13 16:09:35,121  INFO [pool-1-thread-2] (InvokeAnyDemo.java:21) - task2 begin ...
2020-10-13 16:09:35,121  INFO [pool-1-thread-3] (InvokeAnyDemo.java:21) - task3 begin ...
2020-10-13 16:09:38,125  INFO [pool-1-thread-3] (InvokeAnyDemo.java:23) - task3 end .
2020-10-13 16:09:38,125  INFO [main] (InvokeAnyDemo.java:29) - result:task3

线程池的关闭

shutdown()

shutdown()方法会先将线程池的状态改为SHUTDOWN,然后中断空闲的线程,不会再接收新任务,但已提交任务会执行完。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;

/**
 * 线程池的关闭之shutdown()
 */
@Slf4j
public class ShutdownDemo {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {
            log.info("task{} begin ...", i);
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task{} end .", i);
        }));

        threadPoolExecutor.shutdown();

        threadPoolExecutor.submit(() -> log.info("submit task after shutdown"));
    }
}

运行结果如下:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@18769467 rejected from java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Shutting down, pool size = 2, active threads = 2, queued tasks = 8, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.morris.concurrent.threadpool.threadpoolexecutor.api.ShutdownDemo.main(ShutdownDemo.java:30)
2020-10-13 16:54:42,637  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task1 begin ...
2020-10-13 16:54:42,637  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task2 begin ...
2020-10-13 16:54:43,639  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task2 end .
2020-10-13 16:54:43,639  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task1 end .
2020-10-13 16:54:43,640  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task3 begin ...
2020-10-13 16:54:43,640  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task4 begin ...
2020-10-13 16:54:43,641  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task4 end .
2020-10-13 16:54:43,641  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task5 begin ...
2020-10-13 16:54:44,642  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task5 end .
2020-10-13 16:54:44,642  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task6 begin ...
2020-10-13 16:54:45,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task3 end .
2020-10-13 16:54:45,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task7 begin ...
2020-10-13 16:54:46,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task7 end .
2020-10-13 16:54:46,642  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task6 end .
2020-10-13 16:54:46,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task8 begin ...
2020-10-13 16:54:46,642  INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task9 begin ...
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task8 end .
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task10 begin ...
2020-10-13 16:54:47,642  INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task10 end .
2020-10-13 16:54:48,643  INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task9 end .

shutdownNow()

shutdownNow()会先将会先将线程池的状态改为STOP,不会再接收新任务,然后中断所有的线程,如果执行中的任务没有对中断进行处理,那么这个任务将会继续执行直至完成,如果执行中的任务对中断进行的处理,那么将按中断进行执行,最后方法会返回等待队列中未执行的任务。
 

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;

/**
 * 线程池的关闭之shutdownNow()
 */
@Slf4j
public class ShutdownNowDemo {

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {
            log.info("task{} begin ...", i);
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(3));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task{} end .", i);
        }));

        List<Runnable> runnables = threadPoolExecutor.shutdownNow();
        log.info("tasks size:{}", runnables.size());
    }
}

运行结果如下:

2020-10-13 16:55:08,985  INFO [main] (ShutdownNowDemo.java:30) - tasks size:8
2020-10-13 16:55:08,987  INFO [pool-1-thread-2] (ShutdownNowDemo.java:20) - task2 begin ...
2020-10-13 16:55:08,987  INFO [pool-1-thread-1] (ShutdownNowDemo.java:20) - task1 begin ...
2020-10-13 16:55:08,988  INFO [pool-1-thread-2] (ShutdownNowDemo.java:26) - task2 end .
2020-10-13 16:55:08,989  INFO [pool-1-thread-1] (ShutdownNowDemo.java:26) - task1 end .

awaitTermination()

调用shutdown()后,调用线程并不会等待所有任务运行结束,可以利用awaitTermination()方法设置一个超时时间进行阻塞等待,此方法返回了有两种情况,要么所有的任务执行完成,要么超时时间到了。

package com.morris.concurrent.threadpool.threadpoolexecutor.api;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * threadPoolExecutor.awaitTermination()的使用
 */
@Slf4j
public class AwaitTerminationDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        threadPoolExecutor.submit(() -> {
            log.info("task begin ...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task end .");
        });
        threadPoolExecutor.shutdown(); // 关闭线程池

        threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);
        log.info("main thread exit");
    }
}

运行结果如下:

2020-10-13 17:01:45,397  INFO [pool-1-thread-1] (AwaitTerminationDemo.java:16) - task begin ...
2020-10-13 17:01:48,398  INFO [pool-1-thread-1] (AwaitTerminationDemo.java:22) - task end .
2020-10-13 17:01:48,398  INFO [main] (AwaitTerminationDemo.java:27) - main thread exit

创建线程的工厂ThreadFactory

可以自定义ThreadFactory为线程池中的线程取一个有意义的名字,方便后面快速定位问题。

package com.morris.concurrent.threadpool.threadpoolexecutor.construct;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 使用ThreadFactory自定义线程名
 */
@Slf4j
public class ThreadFactoryDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadFactory() {

            private AtomicInteger idx = new AtomicInteger(1);

            private static final String THREAD_NAME_PREFIX = "mythread-pool-";

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, THREAD_NAME_PREFIX + idx.getAndIncrement());
            }
        });

        threadPoolExecutor.submit(()->{
            log.info("task begin ...");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.info("task end .");
        });

        threadPoolExecutor.shutdown();
    }
}

回答:Java线程池Java中的一个重点知识,并且在Java的工作中经常会遇到,因此在面试中也是必问的面试题目。以下是一些常见的Java线程池面试题: 1. 谈谈什么是线程池? 2. 为什么要使用线程池? 3. 你们哪些地方会使用到线程池? 4. 线程池有哪些作用? 5. 线程池创建方式有哪些? 6. 线程池底层是如何实现复用的? 7. ThreadPoolExecutor核心参数有哪些? 8. 线程池创建的线程会一直在运行状态吗? 9. 为什么阿里巴巴不建议使用Executors? 10. 线程池的底层实现原理是什么? 11. 线程池队列满了,任务会丢失吗? 12. 线程池的拒绝策略类型有哪些? 13. 线程池如何合理配置参数? 这些问题涵盖了线程池的基本概念、使用场景、实现原理以及相关的配置和策略等方面的知识。了解这些问题能够帮助面试者更好地理解和应用Java线程池。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [java线程池面试题有哪些?java线程池常见面试题](https://blog.csdn.net/muli525/article/details/123553744)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *3* [(一)【Java精选面试题】线程池底层实现原理(含答案)](https://blog.csdn.net/qq_30999361/article/details/124924343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值