这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战
今天说多线程,若是你对进程和线程还有点迷糊,可以移步至 说说进程和线程 。
要想实现多线程,我们要先学会创建线程。创建线程有这么 3 种方式。
1 继承 Thread 类 。声明一个 Thread 的子类,该类需要重写 Thread 类的 run 方法。然后即可通过 start 方法来启动这个线程。
1 | java复制代码public class Thread1 extends Thread{ |
2 实现 Runnable 接口。声明一个实现 Runnable 接口的子类,重写 run 方法。
1 | typescript复制代码public class TaskNoResult implements Runnable{ |
本质上来说,继承 Thread 类也是在实现 Runnable 接口,启动线程的方式只有通过 Thread 类的 start 方法,start 方法是一个 native 方法,通过这个方法去执行 run 方法,run 方法里面的内容只是线程的执行体罢了。记住,启动线程的方式就一种,那就是通过 Thread 类的 start 方法。
实现 Runnable 接口的优点如下:
- 避免单继承的局限
- 线程代码可以被多个线程共享
- 适合多个线程处理同一个资源的情况
- 使用线程池时,只能放入 Runnable 或 Callable 类型线程。
3 实现 Callable 接口。Callable 接口是在 JDK1.5 中出现的,我们可以通过实现该接口并重写 call 方法来创建线程。
1 | arduino复制代码public class TaskWithResult implements Callable<String>{ |
Runnable 和 Callable 的区别:
- Runnable 重写 run 方法,而 Callable 重写 call 方法。
- Runnable 没有返回值, Callable 有返回值。
- run 方法不能抛出异常,call 方法可以抛出异常。
- 运行 Callable 任务可以得到一个 Future 对象。
1 | java复制代码public interface Future<V> { |
Future 是一个接口,他提供给我们方法来检测当前任务是否已经结束,还可以等待任务结束并且拿到一个结果。通过调用 Future 的 get 方法可以当任务结束后返回一个结果值,如果工作没有结束,则会阻塞当前线程,直到任务执行完毕,我们可以通过调用 cancel 方法来停止一个任务,如果任务已经停止,则cancel 方法会返回 true。如果任务已经完成或已经停止或这个任务无法停止,则 cancel 会返回一个 false。当一个任务被成功停止后,他无法再次执行。 isDone 和 isCancel 方法可以判断当前工作是否完成和是否取消。
我们可以这样理解,Runnable 和 Callable 都是用来创建任务,而我们用线程去驱动执行这个任务,常规的做法像这样:
1 | sql复制代码new Thread(new TaskNoResult("任务1")).start(); |
但是并不推荐这样使用,推荐使用线程池来创建线程进而驱动任务执行。像这样:
1 | vbscript复制代码ExecutorService pool = Executors.newCachedThreadPool(); |
下面奉上一张线程生命周期图,这张图值得好好看看。
简单说一下线程中的几个方法。
start() :启动线程的方法,但是并不保证线程会立即执行。
sleep(long) :暂停线程一段时间,参数为毫秒数。
join() :把指定的线程加入到当前线程执行,等待其执行完毕,可用于控制线程的执行顺序。
yield() :线程让步,只会将 CPU 让给同优先级的线程,但是并不保证一定会让步成功。
最后说一个实际不建议使用的知识点,设置线程的优先级。因为优先级的高低不是决定线程执行顺序的决定因素,所以,千万不要指望设置优先级来控制线程的执行顺序。
1 | scss复制代码t.setPriority(Thread.MIN_PRIORITY); // 最低 1 |
本文转载自: 掘金