推荐链接:
总结——》【Java】
总结——》【Mysql】
总结——》【Redis】
总结——》【Kafka】
总结——》【Spring】
总结——》【SpringBoot】
总结——》【MyBatis、MyBatis-Plus】
总结——》【Linux】
总结——》【MongoDB】
总结——》【Elasticsearch】
Java——》创建线程的方式
- 1. 继承Thread类,重写run方法
- 1.1 步骤
- 1.2 示例
- 1.3 注意
- 2. 实现Runnable接口,重写run方法
- 2.1 步骤
- 2.2 示例
- 2.3 优点
- 3. 实现Callable接口,重写call方法,配合FutureTask
- 3.1 步骤
- 3.2 示例
- 3.3 底层实现
- 3.4 Future对象
- 3.5 实现Runnable接口和实现Callable接口的区别
- 4. 基于线程池创建线程
- 5. 总结
Java中的线程本质上就是一个
Thread对象
。创建线程常用方式:
- 继承Thread类,重写run方法
- 实现Runnable接口
- 实现Callable接口
- 线程池
1. 继承Thread类,重写run方法
1.1 步骤
- 创建Thread类的子类
- 重写run()方法
- 创建线程对象
- 调用start()方法,启动线程
1.2 示例
package org.example;public class CreateThreadForThread {public static void main(String[] args) {System.out.println("main方法执行开始...");// Java中的线程本质上就是一个Thread对象Thread t1 = new MyThread();/*start()方法:1. 启动一个新的线程2. 线程不能启动多次,否则报异常3. 实际下真正开启线程的是CPU,当CPU空闲或分配到此任务的时候,就会创建一个新的线程,然后执行run()方法中的代码*/t1.start();// 如果要创建多个线程,需要创建多个Thread对象Thread t2 = new MyThread();t2.start();Thread t3 = new MyThread();// 显示的调用Thread的run()方法,并没有开启新的线程t3.run();for (int i = 0; i < 100; i++) {System.out.println("main方法的循环..." + i);}System.out.println("main方法执行结束...");}
}
/*** 继承Thread类,重写run方法*/
class MyThread extends Thread{@Overridepublic void run() {System.out.println("子线程执行开始....");for(int i = 0 ; i < 10 ; i ++){System.out.println("子线程的循环:"+i);}System.out.println("子线程执行结束....");}
}
1.3 注意
- 启动线程是使用start()方法而不是run()方法
- 调用run()方法,当前线程直接执行run()方法中的业务逻辑
- 线程不能启动多次
- 如果要创建多个线程,需要创建多个Thread对象
2. 实现Runnable接口,重写run方法
2.1 步骤
- 创建Runable的实现类
- 重写run方法
- 创建Runable实例对象(通过实现类来实现)
- 创建Thread对象,并把第3步的Runable实现作为Thread构造方法的参数
- 调用start方法,启动线程
2.2 示例
package org.example;/*** 创建线程对象:* 方式1:通过Runable实现接口* 方式2:通过Runable匿名内部类* 方式3:通过lambda表达式*/
public class CreateThreadForRunnable {public static void main(String[] args) {System.out.println("main方法执行开始...");MyRunnable myRunnable = new MyRunnable();// 方式1:创建Thread对象的时候传递了一个Runable接口实现Thread t1 = new Thread(myRunnable);// 启动线程t1.start();// 方式2:创建Thread对象的时候传递了一个Runable匿名内部类Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("匿名内部类的循环:" + i);}}});t2.start();// 方式3:创建Thread对象的时候传递了一个lambda表达式Thread t3 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("lambda的循环:" + i);}});t3.start();for (int i = 0; i < 100; i++) {System.out.println("main方法的循环..." + i);}System.out.println("main方法执行结束...");}}/*** 实现Runnable接口,重写run方法*/
class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("子线程执行开始....");for (int i = 0; i < 100; i++) {System.out.println("子线程的循环:"+i);}System.out.println("子线程执行结束....");}
}
2.3 优点
- 可以避免Java单继承带来的局限性
- 适合多个相同的程序代码处理同一个资源的情况,把线程同程序的代码和数据有效的分离,较好的体现了面向对象的设计思想
3. 实现Callable接口,重写call方法,配合FutureTask
前面介绍的两种创建线程的方式都是重写run()方法,而且run()方法是没有返回结果的,也就是main方法是不知道开启的线程什么时候开始执行,什么时候结束执行,也获取不到对应的返回结果。而且run()方法也不能把可能产生的异常抛出。
在
JDK1.5之后
,推出了通过实现Callable接口的方式来创建新的线程,这种方式可以获取对应的返回结果。
3.1 步骤
- 创建Callable的实现类
- 重写call()方法
- 创建Callable实例对象(通过实现类来实现)
- 创建FutureTask实例对象,传入Callable实例对象
- 创建Thread实例对象,传入FutureTask实例对象作为Thread构造方法的参数
- 调用start方法,启动线程
3.2 示例
package org.example;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CreateThreadForCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("main方法执行开始...");//1. 创建MyCallableCallable<Integer> myCallable = new MyCallable();//2. 创建FutureTask,传入CallableFutureTask<Integer> futureTask = new FutureTask<Integer>(myCallable);//3. 创建Thread线程, futureTask本质上是Runable接口的实现Thread t1 = new Thread(futureTask);//4. 启动线程,本质上是执行Runable中的run()方法,只是run()方法中调用了call()方法t1.start();//5. 做一些操作//6. 线程执行完成后返回的结果Integer count = futureTask.get();System.out.println("总和为:" + count);System.out.println("main方法执行结束...");}
}/*** 创建Callable的实现类* 指定Callable的泛型,这个泛型是返回结果的类型*/
class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("子线程执行开始....");int count = 0;for (int i = 0; i < 100; i++) {count += i;}System.out.println("子线程执行结束....");return count;}
}
3.3 底层实现
其实Callable接口底层的实现就是**对Runable接口实现的封装**,线程启动后执行的也是Runable接口实现中的run()方法,只是在run()方法中有调用call()方法。
3.4 Future对象
运行Callable任务可以拿到一个Future对象,表示**异步计算的结果**。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
3.5 实现Runnable接口和实现Callable接口的区别
区别 | 实现Runnable接口 | 实现Callable接口 |
---|---|---|
版本 | JDK1.1之后 | JDK1.5之后 |
规定方法 | run() | call() |
返回值 | 无 | 有,Future.get() |
抛出异常 | 不可以 | 可以 |
加入线程池运行 | ExecutorService.execute() | ExecutorService.submit() |
4. 基于线程池创建线程
追其底层,其实只有一种,实现Runnble
package org.example;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorServiceTest {public static void main(String[] args) {ExecutorService service = Executors.newCachedThreadPool();service.execute(() -> {System.out.println("Hello ThreadPool");});}
}
5. 总结
package org.example;import java.util.concurrent.*;public class CreateThread {public synchronized void m1() {System.out.println(Thread.currentThread().getName() + " m1 ");}static class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello MyThread!");}}static class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Hello MyRunnable!");}}// 需要指定Callable的泛型,这个泛型是返回结果的类型static class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("Hello MyCallable");return "success";}}public static void main(String[] args) throws Exception {// 第1种:继承Thread类,重写run方法new MyThread().start();// 第2种:实现Runnable接口,重写run方法new Thread(new MyRunnable()).start();// 本质是创建Thread对象的时候传递了一个Runable接口实现// 第3种:匿名内部类,重写run方法new Thread(new Runnable(){@Overridepublic void run() {System.out.println("Hello Runnable!");}}).start();// 第4种:lamda表达式new Thread(() -> {System.out.println("Hello Lambda!");}).start();// 第5种:实现Callable接口,重写call方法(运行 + 返回值)FutureTask<String> task = new FutureTask<>(new MyCallable());// FutureTask 本质上是 Runable 接口的实现Thread t = new Thread(task);t.start();// 本质还是执行 Runable中的run方法,只是 run方法调用了call方法System.out.println(task.get());// 获取线程执行完成后返回的结果ExecutorService service = Executors.newCachedThreadPool();// 第6种:线程池,无返回值service.execute(() -> {System.out.println("Hello ThreadPool");});// 第7种:线程池,有返回值Future<String> f = service.submit(new MyCallable());String s = f.get();System.out.println(s);service.shutdown();CreateThread tt = new CreateThread();//1.8之前的写法new Thread(new Runnable() {@Overridepublic void run() {tt.m1();}}).start();//1.8之后的写法:默认线程名称new Thread(()->tt.m1()).start();new Thread(tt::m1).start();//1.8之后的写法:自定义线程名称new Thread(()->tt.m1(), "t1").start();new Thread(tt::m1, "t1").start();}
}