文章目录
一,193-商城业务-异步-异步复习
Java中使用线程的4种方式包括:
- 继承
Thread
类 - 实现
Runnable
接口 - 实现
Callable
接口结合FutureTask
使用 - 使用线程池。
1. 继承Thread
类
继承Thread
类并重写run
方法来定义线程执行的任务。这种方式简单直观,但存在一些限制。
代码示例:
public class ThreadExample1 extends Thread {
@Override
public void run() {
System.out.println("ThreadExample1 is running.");
}
}
// 使用
ThreadExample1 thread = new ThreadExample1();
thread.start();
问题:继承Thread
类会强制要求类继承自Thread
,这限制了类的继承结构,因为Java不支持多重继承。
2. 实现Runnable
接口
通过实现Runnable
接口,可以避免继承Thread
类的限制,因为Java允许一个类实现多个接口。
代码示例:
public class RunnableExample1 implements Runnable {
@Override
public void run() {
System.out.println("RunnableExample1 is running.");
}
}
// 使用
RunnableExample1 runnable = new RunnableExample1();
new Thread(runnable).start();
问题:与继承Thread
类类似,这种方式不能获取线程执行的返回值,也不能很好地控制资源。
3. 实现Callable
接口结合FutureTask
Callable
接口允许任务有返回值,并且可以通过FutureTask
来管理线程的生命周期和结果。
代码示例:
public class CallableExample1 implements Callable<Integer> {
@Override
public Integer call() {
return 100;
}
}
// 使用
FutureTask<Integer> futureTask = new FutureTask<>(new CallableExample1());
new Thread(futureTask).start();
try {
Integer result = futureTask.get(); // 阻塞等待结果
System.out.println("CallableExample1 returned: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
问题:尽管这种方式可以获取返回值,但创建和管理线程的开销仍然存在。
4. 使用线程池
线程池提供了一种高效的方式来管理线程,它可以复用线程,减少创建和销毁线程的开销,同时可以控制并发级别。
代码示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10); // 创建线程池
Runnable task = () -> System.out.println("Task is running in thread pool.");
executorService.submit(task); // 提交任务到线程池
// 执行完毕后关闭线程池
executorService.shutdown();
}
}
优势:
- 资源控制:线程池可以控制并发线程的数量,有效管理资源。
- 性能稳定:通过复用线程,减少了线程创建和销毁的开销,提高了性能。
- 易于管理:线程池提供了统一的接口来提交和管理任务,简化了代码。
在实际开发中,推荐使用线程池来执行多线程任务
,因为它提供了更好的资源管理和性能优势。
前三种方式虽然在某些简单场景下可以使用,但在需要高效资源管理和复杂任务调度的现代应用程序中,它们往往不够灵活和高效。
二,194-商城业务-异步-线程池详解
1,线程池七大参数
线程池(ThreadPoolExecutor
)是Java并发编程中用于管理线程的一个核心组件。它允许你复用线程,从而减少因频繁创建和销毁线程而带来的开销。以下是线程池的七个主要参数,这些参数在创建线程池时可以进行配置:
-
corePoolSize:线程池的基本大小,即在没有任务执行时,线程池中保留的线程数量。如果允许核心线程超时(通过设置
allowCoreThreadTimeOut
为true
),则即使线程池中的线程是空闲的,它们也会在一定时间后终止。 -
maximumPoolSize:线程池中允许的最大线程数。如果任务队列满了,线程池会尝试创建新的线程,直到达到这个数目。
-
keepAliveTime:当线程池中的线程数量超过
corePoolSize
时,多余的空闲线程能等待新任务的最长时间。如果在这个时间内没有新任务到达,这些线程将被终止。 -
unit:
keepAliveTime
参数的时间单位,常见的时间单位有TimeUnit.SECONDS
、TimeUnit.MILLISECONDS
等。 -
workQueue:一个阻塞队列,用于存储等待执行的任务。这个队列只保存通过
execute(Runnable)
方法提交的Runnable
任务。 -
threadFactory:一个线程工厂,用于创建新线程。通过自定义线程工厂,你可以对线程的创建过程进行控制,例如设置线程的名称、优先级等。
-
handler:当线程池饱和(即线程数量达到
maximumPoolSize
且任务队列已满)时,使用的饱和策略。饱和策略定义了如何处理无法立即执行的任务,常见的饱和策略有:ThreadPoolExecutor.AbortPolicy
:抛出RejectedExecutionException
来拒绝新任务的处理。ThreadPoolExecutor.CallerRunsPolicy
:调用执行任务的线程(提交任务的线程)来运行当前任务。ThreadPoolExecutor.DiscardPolicy
:直接丢弃无法处理的任务。ThreadPoolExecutor.DiscardOldestPolicy
:丢弃任务队列中最老的任务,然后尝试再次提交当前任务。
这些参数共同定义了线程池的行为和性能。正确地配置这些参数对于优化应用程序的并发性能至关重要。例如,如果corePoolSize
和maximumPoolSize
设置得太高,可能会导致资源竞争和上下文切换的开销;如果设置得太低,则可能无法充分利用多核处理器的优势。同样,workQueue
的选择也会影响线程池的吞吐量和延迟特性。
2,面试题
下面是一个面试题。
当有100个并发请求进入线程池时,线程池会按照以下步骤进行操作:
-
首先,线程池会尝试使用其核心线程来处理请求。由于核心线程数为7,所以线程池会立即启动7个线程来处理这7个请求。
-
如果还有更多请求进来,并且核心线程都在忙于处理其他请求,那么这些额外的请求会被放入队列中等待处理。由于队列容量为50,所以最多可以存放50个请求。
-
当队列也满了后,线程池会尝试创建新的非核心线程来处理请求。由于最大线程数为20,所以最多可以创建13个非核心线程。
-
如果此时仍然有请求进来,并且所有的线程都在忙于处理其他请求,那么线程池可能会选择拒绝这些请求。具体的行为取决于线程池的饱和策略。
3,Executors能创建的4中线程池
在实际开发过程中,一般用Executors
创建线程池,Executors
相当于一个工厂类,创建各种各样的线程池对象。
-
FixedThreadPool:
- 类型:固定大小的线程池。
- 特点:线程池中的线程数量是固定的。当一个线程完成任务后,它会从队列中获取新的任务来执行。这种线程池能够保证程序的资源占用相对稳定。
- 用途:适用于需要控制线程数量的场景,如后台任务处理、定时任务等。
-
CachedThreadPool:
- 类型:可缓存的线程池。
- 特点:线程池的大小没有限制,可以根据需要动态地调整线程的数量。空闲的线程会在一段时间后自动终止。
- 用途:适用于执行大量短小的任务,如网络I/O操作、计算密集型任务等。
-
SingleThreadExecutor:
- 类型:单线程的线程池。
- 特点:线程池只有一个线程,确保所有任务按照指定顺序执行。
- 用途:适用于需要保证任务顺序执行的场景,例如数据更新、文件写入等。
-
ScheduledThreadPoolExecutor:
- 类型:支持定时任务的线程池。
- 特点:线程池可以调度任务以定期或延迟的方式执行。
- 用途:适用于需要定期执行任务或延时执行任务的场景,如计划任务、周期性检查等。
下面是这四种线程池的具体实现方式:
FixedThreadPool
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads);
CachedThreadPool
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
SingleThreadExecutor
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledThreadPoolExecutor
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(nThreads);
其中,nThreads
表示线程池中的线程数量。对于 newFixedThreadPool
和 newScheduledThreadPool
,这个值是必需的,而对于 newSingleThreadExecutor
和 newCachedThreadPool
,则不需要指定线程数量。