Java多线程实现的方式有4种:
1. 继承Thread类,重写run方法。
2. 实现Runnable接口,实现run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target。
3. 通过线程池创建线程,即Executor。
4. 实现Callable线程接口(有返回值)。
- 前面3种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果。
- ”实现Callable线程接口“这个方式可以归结成一类:有返回值,实现Callable接口,就要实现call方法,这个方法的返回值可由泛型指定。
1 继承Thread类,重写run方法
1、Thread类位于java.lang包中,Thread的每个实例对象就是一个线程,它的子类的实例也是一个线程。
2、我们通过Thread类或它的派生类才能创建线程的实例并启动一个新的线程。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("子线程run()方法执行----");
}
}
2 实现Runnable接口,实现run方法
1、通过继承Thread类来创建线程,有一个缺点,如果我们的类已经从一个类继承,则无法再继承Thread类。
2、我们可以通过声明自己的类实现Runnable接口来创建线程。Runnable接口只有一个方法run(),我们的类需要实现这个方法。
3、实现Runnable接口来创建线程的步骤如下:
(1)定义Runnable接口的实现类,并实现该接口的run()方法。
(2)创建Runnable实现类的实例,并以此实例作为Thread类的target参数来创建Thread线程对象,该Thread对象才是真正的线程对象。
3 使用线程池的Executor(线程执行器)
1、java SE5的java.util.concurrent包中的Executor(执行器)将为你管理Thread对象,从而简化多线程编程。
2、Executor在客户端和任务之间提供了一个中间层,与客户端直接执行任务不同,这个中介对象将执行任务。
3、Executor允许你管理异步任务的执行,无须显式地管理线程的生命周期。Executor在Java SE5/6是启动线程的优选方法。
4、java里面的线程池的顶级接口是Executor,Executor并不是一个线程池,而只是一个执行线程的工具,而真正的线程池是ExecutorService。
使用demo
创建一个LiftOff类:
public class LiftOff extends Thread {
private String name;
public LiftOff(String name) {
this.name = name;
}
@Override
public void run() {
super.run();
System.out.println("LiftOff.run(): the name is " + name);
}
}
测试代码:
/**
* @author chenlw
* @date 2020/02/07
*/
public class ExecutorTester {
public static void main(String[] args) {
testCachedThreadPool();
}
public static void testCachedThreadPool() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new LiftOff("LiftOff" + i));
}
executorService.shutdown();
}
}
通常,单个的Executor被用来创建和管理系统中的任务。
对shutdown()方法的调用可以防止新任务被提交到这个Executor,当前线程(在本例当中指main()主线程)将继续运行在shutdown()被调用前提交的所有任务。这个程序将在Executor中的所有任务完成后尽快推出。
Executor常用的类型有以下几种。
CachedThreadPool、FixedThreadPool、SingleThreadExecutor。
3.1 CachedThreadPool(可缓存线程池程)
CachedThreadPool在程序运行当中通常会创建与任务数量相同的线程,然后在它回收线程的时候停止创建新线程,因此它是合理的Executor的首选。
3.2 FixedThreadPool(定长线程池)
1、一次性预先执行代价高昂的线程分配,因而也就可以限制线程的数量了。这可以节省时间。
2、FixedThreadPool会创建有数量限制的线程池,这样可以控制资源的开销。
3、使用场景:如果希望创建特定数量的线程池,可以使用FixedThreadPool,初始化时设定线程池的数量。
3.3 SingleThreadExecutor(单线程化的线程池)
1、它就像线程数量为1的FixedThreadPool。这对于希望在另一个线程中连续运行的事物(长期存活的任务)来说是很有用的,例如监听进入套接字连接的任务。
2、如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有的任务都将使用相同的线程。
3、SingleThreadExecutor会序列化所有提交给它的任务,并且会维护它自己内部的悬挂任务队列。
使用线程池的优点
1.重用线程池的线程,避免因为线程的创建和销毁锁带来的性能开销。
2.有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞。
3.能够对线程进行简单的管理,并提供一下特定的操作如:可以提供定时、定期、单线程、并发数控制等功能。
4 实现Callable接口(线程方法有返回值)
1、如果希望线程方法处理完任务返回一些数据,那么可以使用Callable接口。
2、在Java SE5引入的Callable接口是一种具有类型参数的泛型,它的类型参数表示的是方法call()返回的类型。
3、下面是一个简单示例:
class TaskWithResult implements Callable<String> {
private int id;
public TaskWithResult(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
return "result of TaskWithResult is " + id;
}
}
/**
* @author chenlw
* @date 2020/02/07
*/
public class CallableTester {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<String>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
// submit()方法会返回Future对象,它用Callable返回结果的特定类型进行了参数化。
results.add(executorService.submit(new TaskWithResult(i)));
}
for (Future<String> future : results) {
try {
System.out.println("isDone(): "+future.isDone()+", "+" get():"+future.get());
} catch (InterruptedException e1) {
System.out.println(e1.getMessage());
} catch (Exception e2) {
System.out.println(e2);
} finally {
executorService.shutdown();
}
}
}
}
运行结果:
isDone(): true, get():result of TaskWithResult is 0
isDone(): true, get():result of TaskWithResult is 1
isDone(): true, get():result of TaskWithResult is 2
isDone(): true, get():result of TaskWithResult is 3
isDone(): true, get():result of TaskWithResult is 4
isDone(): true, get():result of TaskWithResult is 5
isDone(): true, get():result of TaskWithResult is 6
isDone(): true, get():result of TaskWithResult is 7
isDone(): true, get():result of TaskWithResult is 8
isDone(): true, get():result of TaskWithResult is 9
- submit()方法会返回Future对象,它用Callable返回结果的特定类型进行了参数化。
- 我们可以用isDone()方法来查询Future是否已经完成。当任务完成时,它会有一个结果。
- 另外,我们也可以直接调用get()方法来获取结果,在这种情况下,get()方法可能会被阻塞,直到线程返回结果数据。
来源:CSDN
作者:举世武双
链接:https://blog.csdn.net/qq_33721382/article/details/104228611