Java 多线程

1、优先级

每个Java线程都有对应的优先级,高优先级的线程比低优先级的线程有更多的执行机会。新创建的Java线程默认的优先级是5, 最低是1,最高是10;

  1. /**
  2. * The minimum priority that a thread can have.
  3. */
  4. public final static int MIN_PRIORITY = 1;
  5. /**
  6. * The default priority that is assigned to a thread.
  7. */
  8. public final static int NORM_PRIORITY = 5;
  9. /**
  10. * The maximum priority that a thread can have.
  11. */
  12. public final static int MAX_PRIORITY = 10;

Note:如果在一个线程中创建一个新的线程,新线程的默认优先级和所在线程的优先级保持一致。

2、daemon 或 非daemon

一个Java线程可以是daemon的也可以是非daemon的,JVM在启动的时候启动了main线程,该线程是非daemon;JVM在下面2的情况下会退回:

  1. 调用Runtime.exit()方法
  2. 所有非daemon的线程都结束了。

    3、创建线程的方式

    有2种创建线程的方式,一种是继承Thread类;一种是直接new一个Thread对象,并在构造函数中传入一个Runnable对象。

    继承

    ```java class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) {

    1. this.minPrime = minPrime;

    }

    public void run() {

    1. // compute primes larger than minPrime

    } } PrimeThread p = new PrimeThread(143); p.start();

//new class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; }

  1. public void run() {
  2. // compute primes larger than minPrime
  3.  . . .
  4. }

} PrimeRun p = new PrimeRun(143); new Thread(p).start();

  1. <a name="b3jl1"></a>
  2. ### 4、线程的状态
  3. JDK中关于线程状态的定义:
  4. - `new `: 一个新创建的,没有调用`start`方法的线程处于该状态;
  5. - `RUNNABLE `:可执行的,在JVM中是处于执行中,但是在等待其它的操作系统资源,如cpu资源;
  6. - `BLOCKED `:阻塞的,一个线程在进入(或再次进入)一个同步方法或同步块时等待监视器锁时处于该状态。
  7. - `WAITING `:等待,一个线程在调用了如下的方法时会处于等待状态:<br />1.`Object.wait`<br />2.`Thread.join` //正在主线程中调用一个一个线程的`join`方法,在主线程处于等待状态<br />3.`LockSupport#park()`
  8. - `TIMED_WAITING` 等待的,不过该等待状态是有时间限制的。一个线程调用下面的方法会进入该状态:<br />1.`Thread.sleep`<br />2.`Object.wait`<br />3.`Thread.join`<br />4.`LockSupport.parkNanos`<br />5.`LockSupport.parkUntil`
  9. - `TERMINATED` 终止,当一个线程执行完成后处于该状态
  10. <a name="x9Raa"></a>
  11. ### 5、线程的调度
  12. 理想的情况下,每个程序的线程都拥有一个专属于自己的处理器;在计算机还不能拥有几千,甚至几百万CPU处理器的情况下,多个线下需要共享仅有的cpu资源,如果分配线程执行所需的cpu资源就需要线程调度器的调度了。在操作系统层面有自己的线程调度器,在JVM中也存在Java线程调度器。<br />在Java线程调度中有2点比较重要:
  13. 1. Java规范并没有强制要求每个JVM按照特定的调度规则调用线程,或者必须包含一个线程调度器。线程调度的实现完全是依赖平台的。
  14. 1. 在编写Java多线程代码时,唯一需要考虑的是不要让一个线程大量的占用cpu时间(eg.死循环)。
  15. 大多数平台上JVM的线程调度是依赖操作系统本身的线程调度器的,每个线下有不同的优先级,在基于时间片的规则下,高优先级的线程拥有更多的CPU执行机会;相同优先级的线程可以按照FIFO调度。
  16. <a name="MeNg7"></a>
  17. ### 6、`Runnable`、`Callable`、`Future` 和 `FutureTasek`的区别
  18. Java中存在`Runnable`、`Callable`、`Future`、`FutureTask`这几个与线程相关的类或者接口,在Java中也是比较重要的几个概念,通过下面的简单示例来了解一下它们的作用与区别。
  19. <a name="CwkIr"></a>
  20. #### 6.1 `Runnable`
  21. `Runnable` 应该是这几个类使用的最多的一个。JDK的文档说明:如果一个类的实例想要通过一个线程来执行,则该类应该实现`Runnable`接口。`Runnable`被设计用来对那些处于`active`状态时会执行代码的对象提供一个统一的协议。例如`Thread`类就实现了`Runnable`接口。
  22. ```java
  23. public interface Runnable {
  24. public abstract void run();
  25. }

6.2 Callable

Callable 表示一个可以携带返回结果的任务。该接口的实现类需要一个没有参数的call方法。Callable 和 Runnable类似,都是被设计用来被另外一个线程执行的任务。但是Runnable不能返回一个结果,且不能抛出一个checked异常。

  1. public interface Callable<V> {
  2. /**
  3. * Computes a result, or throws an exception if unable to do so.
  4. *
  5. * @return computed result
  6. * @throws Exception if unable to compute a result
  7. */
  8. V call() throws Exception;
  9. }

6.3 Future

一个Futrue表示一个异步计算的结果。它提供了一系列方法,用来检测计算是否完成,获取计算结果,等待计算结果等。只能通过get方法获取计算结果;如果计算没有完成,在必要条件下,则get方法一直等待,直到任务完成。cancel方法用来取消任务。其它的方法都是用来测试计算是否完成,或是否取消。一旦计算完成后,就不可以被取消。如果想使用Future,但并不需要返回一个结果,则可以使用Future<?>并返回null。
一个简单的例子(JDK原文):

  1. class App {
  2. ExecutorService executor = ...
  3. ArchiveSearcher searcher = ...
  4. void showSearch(final String target) throws InterruptedException {
  5. Future<String> future = executor.submit(new Callable<String>() {
  6. public String call() {
  7. return searcher.search(target);
  8. }});
  9. displayOtherThings();
  10. try{
  11. displayText(future.get()); // use future
  12. }catch(ExecutionException ex){
  13. cleanup(); return;
  14. }
  15. }
  16. }

6.4 FutureTask

FutureTask 表示一个可以取消的异步计算任务。它实现了Runnable接口和Future接口。FutureTask 可以用来包装RunnableCallable对象。应该FutureTask实现了Runnable接口。同时可以通过被提交到Executor去执行。

  1. public FutureTask(Callable<V> callable) {
  2. if (callable == null)
  3. throw new NullPointerException();
  4. this.callable = callable;
  5. this.state = NEW; // ensure visibility of callable
  6. }
  7. public FutureTask(Runnable runnable, V result) {
  8. this.callable = Executors.callable(runnable, result);
  9. this.state = NEW; // ensure visibility of callable
  10. }

可以看到,Runnable注入会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。该适配函数的实现如下 :

  1. public static <T> Callable<T> callable(Runnable task, T result) {
  2. if (task == null)
  3. throw new NullPointerException();
  4. return new RunnableAdapter<T>(task, result);
  5. }

RunnableAdapter适配器

  1. /**
  2. * A callable that runs given task and returns given result
  3. */
  4. static final class RunnableAdapter<T> implements Callable<T> {
  5. final Runnable task;
  6. final T result;
  7. RunnableAdapter(Runnable task, T result) {
  8. this.task = task;
  9. this.result = result;
  10. }
  11. public T call() {
  12. task.run();
  13. return result;
  14. }
  15. }

7、总结

由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行。
并且还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。因此FutureTask既是FutureRunnable,又是包装了Callable(如果是Runnable最终也会被转换为Callable), 它是这两者的合体。