Java创建线程的三种方式和线程池的使用
创建新线程
最常用的线程创建方式是使用 Runnable
接口
其次就是使用 Thread
类创建新线程
如果需要新线程有返回值则使用 Callable
和 Future
接口创建新线程
线程池的使用
在JDK1.5后由 Executors工厂类
可直接创建线程池
使用线程池可以合理地对系统资源进行管理
详细了解多线程↓ ↓ ↓ (写的多都没人看)
1.继承 Thread 类
实现步骤
1.新建一个 Thread 类的子类 public class Xxx extends Thread { }
2.在 Thread 类的子类中重写 run 方法
,设置线程任务(开启线程要干什么)
3.在测试类中创建 Thread 类的子类对象
4.调用 Thread 类的start
方法 ,开启新的线程,自动执行run
方法
代码实例
1.创建一个 Thread 类的子类
public class NewThread extends Thread{
//重写 run 方法,设置线程(开启线程要做什么)
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("run"+i);
}
}
}
2.创建 Thread 类的子类,并开启线程
【PS】是调用 start 方法开启线程,而不是调用 run 方法。调用start方法,会自动执行run方法
public class Demo {
public static void main(String[] args) {
NewThread nt = new NewThread();
nt.start();
//nt.run();//调用run方法不会开启线程
for (int i = 0; i < 5; i++) {
System.out.println("main"+i);
}
}
}
程序运行结果:
2.实现 Runnable 接口
实现步骤
1.新建 Runnable 的实现类
2.重写 Runnable 的 run 方法
3.通过 Thread 的构造方法,将 Runnable 的实现类对象传递给 Thread 类
4.通过 Thread 对象调用start方法开启线程
因为 Runnable 接口中没有 start 方法
代码实例
1.新建Runable的实现类
public class NewThread implements Runnable{
@Override
public void run() {
System.out.println("使用Runnable接口创建线程");
}
}
2.开启新线程
public class Demo {
public static void main(String[] args) {
//创建 Runable 的实现类对象
NewThread nt = new NewThread();
//创建 Thread 对象,并通过构造方法将实现类对象传递给Thread类
Thread t = new Thread(nt);
//调用 start 方法开启新线程
t.start();
//=======上方三行代码和下方一行代码是等价的=========
new Thread(new NewThread()).start();//使用 匿名对象 开启线程避免繁琐
System.out.println("main主线程");
}
}
程序运行结果:
两条语句的打印顺序依旧是随机执行,不再截图演示
3.使用 Callable 和 Future 接口
通过此方式创建线程,可以获取新线程任务的返回值
实现步骤
1.创建 Callable<V> 接口的实现类
2.在实现类中重写 Callable 接口中的 call 方法
(注意这里不是 run 方法)
3.创建 FutureTask<V> 类的对象
4.创建 Thread 类对象,将 FutureTask 对象传递到 Thread 的构造方法
5.使用 Thread 对象调用 start 方法,开启新线程
代码实例
1.新建Callable的实现类,并重写call方法
public class CallableThread implements Callable<Integer>{
//重写call方法,编写线程任务
//call方法的返回值类型由Callable接口的泛型决定
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
return i;
}
}
2.开启新线程
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建Callable实现类对象
CallableThread ct = new CallableThread();
//创建FutureTask对象,将Callable实现类对象传递到其构造方法中
FutureTask<Integer> ft = new FutureTask<>(ct);
//将FutureTask对象传递给Thread对象,并调用start方法开启新线程
new Thread(ft).start();
//main方法的线程任务
int i = 0;
for (; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
//获取call方法的返回值
Integer num = ft.get();
System.out.println("call方法的返回值为:" + num);
}
}
程序运行结果
【tips】
传递给
Callable<V>接口的泛型是作为call方法返回值类型的
- 获取新线程任务的返回值,可以
使用
FutureTask的
get方法
- get方法存在两个异常,分别是ExecutionException, InterruptedException
需要进行处理(throws或者try/catch)
概念
4.线程池
线程池:可以理解为存放线程的集合,需要开启新线程时,从线程池里边直接取,用完再放回线程池
线程池的使用步骤:
1.使用线程池 Executors 工厂类里的 newFixedThreadPool 静态方法生产一个指定数量的线程池
2.创建一个实现类实现 Runnable 接口,重写run方法,设置线程任务
3.调用 ExecutorService 接口中的 submit 方法,传递线程任务(Runnable的实现类),开启线程
4.调用 ExecutorService 接口中的 shutdown 方法销毁线程池(不建议使用)
代码实例:
public class Demo01 {
public static void main(String[] args) {
//开启线程池,并放入 5 个线程
ExecutorService executorService = Executors.newFixedThreadPool(5);
int i = 1;
//使用while循环从线程池开启 10 个线程
//开启 10 个线程的目的是,观察线程的复用,体现线程池的价值
while(i<=10){
i++;
//开启一个线程
executorService.submit(new Thread(() -> {
System.out.println("线程池开启线程!"+Thread.currentThread().getName());
}));
}
//销毁线程池
executorService.shutdown();
}
}
运行结果:
线程池的优势
减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。