Java 多线程基础 创建线程的四种方式 线程池介绍
创建线程的四种方式
1.继承 Thread 类
继承 Thread
类,重写 run()
方法
class MyThread extends Thread{
public void run(){
while (true){
System.out.println("继承 Thread ");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
简化写法
匿名内部类写法
Thread thread = new Thread(){
public void run(){
while (true){
System.out.println("继承 Thread ");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
lambda
表达式写法
Thread thread = new Thread(()->{
while (true){
System.out.println("继承 Thread ");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
匿名内部类和lambda
写法看着差不多,其实它们的差别还是很大的:
-
使用匿名内部类的写法 内部类是一个继承了
Thread
的类 -
使用
lambda
写法,实际是传入了一个Runnable
接口的实现类
我们编译ThreadTest
类后会得到匿名内部类的字节码文件反编译字节码文件可以看到它是继承 Thread
的
run() 和 start() 的区别
可以看到直接调用run()
方法并不会启动线程,调用start()
才会启动线程
2.实现 Runnable 接口
实现Runnable
接口 实现 run() 方法
,将实现类对象作为参数传给Thread
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable");
}
}
简化写法
匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("实现Runnable");
}
};
lambda
Runnable runnable = () -> System.out.println("实现Runnable");
3.实现 Callable 接口
Callable
接口 和 Runnable
接口 最大的区别就是 Callable
接口的 call
方法可以返回一个结果,该结果可以是任何类型,例如整数、字符串、对象等。而Runnable
接口的 run
方法没有返回值,因此线程任务无法返回结果。
Callable
接口的任务通常与 Future
接口一起使用,Future
允许你获取任务的执行结果。
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(myCallable);
Thread thread = new Thread(task);
thread.start();
Integer integer = task.get();
System.out.println(integer);
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
return 10;
}
}
简化写法
FutureTask<Integer> task = new FutureTask<>(()-> 10);
Thread thread = new Thread(task);
thread.start();
Integer integer = task.get();
4.使用线程池 ⭐
选择一个适当的线程池类型并创建线程池。
创建实现 Runnable
或 Callable
接口的任务,并将其提交给线程池执行。
// 创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 任务
Runnable runnable = ()-> System.out.println("runnable");
Callable callable = () -> {
System.out.println("callable");
return "callable";
};
FutureTask<Integer> task = new FutureTask<>(()-> 10);
// 提交任务
executorService.submit(runnable);
executorService.submit(callable);
executorService.submit(task);
// 获取任务返回值
System.out.println(task.get());
// 关闭线程池
executor.shutdown();
线程池介绍
Java线程池是一种用于管理和重用线程的机制,它有助于提高多线程应用程序的性能、可维护性和可扩展性。Java提供了java.util.concurrent
包,其中包含多种线程池实现,能够在多线程应用程序中有效地管理线程的生命周期。以下是Java线程池的详细介绍:
线程池的优点:
-
线程重用:线程池可以重用已经创建的线程,减少线程的创建和销毁开销,提高性能。
-
线程管理:线程池提供了一种更方便的方式来管理线程的生命周期,包括创建、启动、暂停、恢复和停止线程。
-
资源管理:线程池可以限制同时运行的线程数量,从而控制系统资源的使用。
-
任务队列:线程池通常包含一个任务队列,可以存储待执行的任务,从而实现任务的排队和调度。
-
线程池参数调整:你可以调整线程池的参数,如线程数量、等待队列大小,以适应不同的工作负载。
Java线程池的主要类和接口:
-
Executor
:是线程池框架的根接口,它定义了一个execute(Runnable command)
方法,用于提交任务。 -
ExecutorService
:是Executor
的子接口,它扩展了任务的生命周期管理功能,包括提交任务、关闭线程池、等待任务完成等操作。 -
ThreadPoolExecutor
:是ExecutorService
接口的一个具体实现,它提供了丰富的线程池配置选项,如核心线程数、最大线程数、任务队列等。 -
Executors
:是一个工厂类,提供了用于创建不同类型的线程池的静态方法。它是创建线程池的便捷方式。
常见类型的线程池:
-
FixedThreadPool(固定大小线程池):该线程池维护固定数量的线程,适用于处理固定数量的任务。
-
CachedThreadPool(可缓存线程池):该线程池根据任务的需求自动创建和销毁线程,适用于处理大量短期任务。
-
SingleThreadExecutor(单线程线程池):该线程池只包含一个线程,适用于按顺序执行任务的场景。
-
ScheduledThreadPool(定时任务线程池):该线程池用于执行定时任务或周期性任务,支持定时执行和周期性执行任务。
使用线程池的基本步骤:
-
创建线程池:选择适当类型的线程池,通常使用
Executors
工厂类创建线程池。 -
提交任务:创建实现
Runnable
或Callable
接口的任务,然后通过线程池的submit
或execute
方法提交任务。 -
关闭线程池:在不再需要线程池时,通过调用线程池的
shutdown
方法来关闭线程池,确保线程资源得以释放。 -
处理任务结果(可选):如果使用
Callable
任务,可以通过Future
对象获取任务的结果。
线程池参数
ThreadPoolExecutor
是 Java 中用于自定义线程池的主要类,它提供了广泛的参数选项,以允许你配置线程池的行为以满足特定需求。以下是 ThreadPoolExecutor
构造函数的详细参数介绍:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 线程空闲超时时间
TimeUnit unit, // 线程空闲超时时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
-
corePoolSize
(核心线程数):这是线程池中保持活动的最小线程数。即使线程处于空闲状态,核心线程也不会被销毁。它定义了线程池的基本容量。 -
maximumPoolSize
(最大线程数):这是线程池允许的最大线程数。当工作队列已满,并且活动线程数小于最大线程数时,线程池会创建新的线程以处理任务。通常,最大线程数应大于或等于核心线程数。 -
keepAliveTime
(线程空闲超时时间):这是一个时间段,在该时间段内,当线程处于空闲状态,它可能被终止并从线程池中移除,以节省资源。 -
unit
(线程空闲超时时间单位):指定了keepAliveTime
参数的时间单位,通常是秒、毫秒等。 -
workQueue
(任务队列):这是一个用于存储等待执行任务的队列。可以选择不同类型的队列,如有界队列、无界队列、优先级队列等,以根据需求控制任务排队策略。 -
threadFactory
(线程工厂):用于创建新线程的工厂。你可以提供自定义的线程工厂以定制线程的名称、优先级等。 -
handler
(拒绝策略):当任务无法被执行时,这是一个处理策略。它定义了如何处理任务拒绝,例如丢弃、抛出异常、阻塞等。
这些参数允许根据具体的应用需求和性能要求来配置线程池。通过调整这些参数,可以创建适合不同工作负载和系统资源的线程池。要使用 ThreadPoolExecutor
,可以调用其构造函数来创建自定义的线程池实例,然后将任务提交给线程池以执行。