快速了解多线程的一些常见知识点
在Java编程中,多线程是非常重要知识点. 关于Java线程有些不可不知的知识点需要牢记,下面就介绍了这些知识点.
1. 优先级
每个Java线程都有对应的优先级,高优先级的线程比低优先级的线程有更多的执行机会。新创建的Java线程默认的优先级是5, 最低是1,最高是10;
1 | /** |
Note:如果在一个线程中创建一个新的线程,新线程的默认优先级和所在线程的优先级保持一致。
2. daemon 或 非daemon
一个Java线程可以是daemon的也可以是非daemon的,JVM在启动的时候启动了main线程,该线程是非daemon;JVM在下面2的情况下会退回:
调用
Runtime.exit()
方法所有非daemon的线程都结束了。
3. 创建线程的方式
有2种创建线程的方式,一种是继承Thread
类;一种是直接new一个Thread对象,并在构造函数中传入一个Runnable对象。
继承
1 | class PrimeThread extends Thread { |
4. 线程的状态
JDK中关于线程状态的定义:
new : 一个新创建的,没有调用start方法的线程处于该状态;
RUNNABLE :可执行的,在JVM中是处于执行中,但是在等待其它的操作系统资源,如cpu资源;
BLOCKED :阻塞的,一个线程在进入(或再次进入)一个同步方法或同步块时等待监视器锁时处于该状态。
WAITING :等待,一个线程在调用了如下的方法时会处于等待状态:
1.Object.wait
2.Thread.join //正在主线程中调用一个一个线程的join方法,在主线程处于等待状态
3.LockSupport#park()TIMED_WAITING 等待的,不过该等待状态是有时间限制的。一个线程调用下面的方法会进入该状态:
1.Thread.sleep
2.Object.wait
3.Thread.join
4.LockSupport.parkNanos
5.LockSupport.parkUntilTERMINATED 终止,当一个线程执行完成后处于该状态
5. 线程的调度
理想的情况下,每个程序的线程都拥有一个专属于自己的处理器;在计算机还不能拥有几千,甚至几百万CPU处理器的情况下,多个线下需要共享仅有的cpu资源,如果分配线程执行所需的cpu资源就需要线程调度器的调度了。在操作系统层面有自己的线程调度器,在JVM中也存在Java线程调度器。
在Java线程调度中有2点比较重要:
Java规范并没有强制要求每个JVM按照特定的调度规则调用线程,或者必须包含一个线程调度器。线程调度的实现完全是依赖平台的。
在编写Java多线程代码时,我们唯一需要考虑的是不要让一个线程大量的占用cpu时间(eg.死循环)。
大多数平台上JVM的线程调度是依赖操作系统本身的线程调度器的,每个线下有不同的优先级,在基于时间片的规则下,高优先级的线程拥有更多的CPU执行机会;相同优先级的线程可以按照FIFO调度。
6. Runnable、Callable、Future 和 FutureTasek的区别
Java中存在Runnable、Callable、Future、FutureTask这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,我们通过下面的简单示例来了解一下它们的作用与区别。
6.1 Runnable
Runnable 应该是这几个类我们使用的最多的一个。JDK的文档说明:如果一个类的实例想要通过一个线程来执行,则该类应该实现Runnable接口。Runnable被设计用来对那些处于active状态时会执行代码的对象提供一个统一的协议。例如Thread类就实现了Runnable接口。
1 | public interface Runnable { |
6.2 Callable
Callable 表示一个可以携带返回结果的任务。该接口的实现类需要一个没有参数的call方法。Callable 和 Runnable类似,都是被设计用来被另外一个线程执行的任务。但是Runnable不能返回一个结果,且不能抛出一个checked异常。
1 | public interface Callable<V> { |
6.3 Future
一个Futrue表示一个异步计算的结果。它提供了一系列方法,用来检测计算是否完成,获取计算结果,等待计算结果等。只能通过get
方法获取计算结果;如果计算没有完成,在必要条件下,则get方法一直等待,直到任务完成。cancel
方法用来取消任务。其它的方法都是用来测试计算是否完成,或是否取消。一旦计算完成后,就不可以被取消。如果你想使用Future,但并不需要返回一个结果,则可以使用Future>
并返回null。
一个简单的例子(JDK原文):
1 | class App { |
6.4 FutureTask
FutureTask 表示一个可以取消的异步计算任务。它实现了Runnable接口和Future接口。FutureTask 可以用来包装Runnable和Callable对象。应该FutureTask实现了Runnable接口。同时可以通过被提交到Executor去执行。
1 | public FutureTask(Callable |
可以看到,Runnable注入会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。该适配函数的实现如下 :
1 | public static |
RunnableAdapter适配器
1 | /** |
7. 总结
由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。
并且还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。因此FutureTask既是Future、
Runnable,又是包装了Callable(如果是Runnable最终也会被转换为Callable ), 它是这两者的合体。
source: //leokongwq.github.io/2016/10/16/java-runnable-callable-future.html
喜欢,在看