关于java:Java多线程的实现方式

 有多少种实现线程的办法?

  1. 从不同的角度看,会有不同的答案。
  2. 典型答案是两种,别离是实现 Runnable 接口和继承 Thread 类,而后具体开展说;
  3. 然而,咱们看原理,其实Thread类实现了 Runnable 接口,并且看 Thread 类的 run 办法,会发现其实那两种实质都是一样的,run 办法的代码如下:
@Override
public void run() {
    if (target != null) {
        target.run();
    }
}

办法一和办法二,也就是 “继承Thread类而后重写 run()” 和 “实现 Runnable 接口并传入 Thread 类” 在实现多线程的实质上,并没有区别,都是最终调用了 start() 办法来新建线程。这两个办法的最次要区别在于 run() 办法的内容起源:

办法一:最终调用 target.run();

办法二:run() 整个都被重写

  1. 而后具体开展说其余形式;

还有其余的实现线程的办法,例如线程池等,它们也能新建线程,然而细看源码,从没有逃出过实质,也就是实现 Runnable 接口和继承 Thread 类。

  1. 论断:咱们只能通过新建 Thread 类这一种形式来创立线程,然而类外面的 run 办法有两种形式来实现,第一种是重写 run 办法,第二种实现 Runnable 接口的 run 办法,而后再把该 runnable 实例传给 Thread 类。除此之外,从外表上看线程池、定时器等工具类也能够创立线程,然而它们的实质都逃不出方才所说的范畴。

以上这种形容比间接答复一种、两种、多种都更精确。

 实现 Runnable 接口相比于继承 Thread 类的长处:

  1. 从代码架构角度: 具体的工作 (run 办法) 应该和 “创立和运行的机制 (Thread 类) ” 解耦, 用 Runnable 对象能够实现解耦.
  2. 实现继承 Thread 的形式的话, 那么每次想新建一个工作, 只能新建一个独立的线程, 而这样做的损耗会比拟大(比方从头开始创立一个线程、执行结束当前再销毁等。如果线程的理论工作内容,也就是 run() 函数里只是简略的打印一行文字的话,那么可能线程的理论工作内容还不如损耗来的大)。如果应用 Runnable 和线程池, 能够重复利用同一个线程, 就能够大大减小这样的损耗.
  3. 继承 Thread 类当前,因为 Java 语言不反对双继承, 这样就无奈再继承其余的类, 限度了可扩展性.

因而通常咱们优先选择 Runnable 接口.

 FutureTask、线程池以及定时器属于多线程的实现形式吗?

多线程的实现形式,在代码中写法变幻无穷,但其本质万变不离其宗。

以上的观点之所以谬误,是因为他们都只不过是包装了 new Thread(),咱们如果把能新建线程的类都称作是一种实现线程的办法,那么就太流于外表了,而没有了解到底层的原理。

而随着 JDK 的倒退,这样的类会越来越多,咱们必定也没方法相熟每一种具备新建线程能力的类,因为有些类基本不罕用。

 线程池:

以下是应用线程池实现线程的代码

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            executorService.submit(new Task() {
            });
        }
    }
}
  

class Task implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    System.out.println(Thread.currentThread().getName());
    }
}
public Thread newThread(Runnable r) {
    Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
    if (t.isDaemon())
        t.setDaemon(false);
    if (t.getPriority() != Thread.NORM_PRIORITY)
        t.setPriority(Thread.NORM_PRIORITY);
    return t;
}

 FutureTask:

以下是应用 FutureTask 类实现线程的代码

public class FutureTaskDemo {
    public static class CallerTask implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello";
        }
    }
  
    public static void main(String[] args) {
        // 创立异步工作
        FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
         // 启动线程, 调用 futureTask 中 run()
         new Thread(futureTask).start();
         try {
             String result = futureTask.get();
             System.out.println(result);
         } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
         }
    }
}

从源码中能够看到 FutureTask 类实现了 RunnableFuture 接口

public class FutureTask<V> implements RunnableFuture<V> {

而 RunnableFuture 接口继承了 Runnable 接口

public interface RunnableFuture<V> extends Runnable, Future<V> {
     /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
     void run();
}

其中, futureTask 实例实现了 Runnable 接口, 并传入 Thread 的构造函数中, 也就是说在执行 start() 的时候会调用 futureTask 中的 run() .

     // 创立异步工作
     FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
     // 启动线程
     new Thread(futureTask).start();

以下是 FutureTask 的局部构造函数和 run() 源码

public FutureTask(Callable<V> callable) {
     if (callable == null)
        throw new NullPointerException();
     this.callable = callable;
     this.state = NEW; // ensure visibility of callable
}

 ...
 
 public void run() {
     if (state != NEW ||
         !RUNNER.compareAndSet(this, null, Thread.currentThread()))
         return;
     try {
         Callable<V> c = callable;
         if (c != null && state == NEW) {
            V result;
         boolean ran;
     try {
         result = c.call();
         ran = true;
     } catch (Throwable ex) {
         result = null;
         ran = false;
         setException(ex);
     }
     if (ran)
        set(result);
}

 ...

咱们本人实现的 CallerTask 类作为 Callable 对象传入 FutureTask 构造函数中, 紧接着在 run() 中会去调用传进来的 callable 对象的 call(), 也就是咱们本人实现的 call(), 并将后果应用 set() 存到 outcome 中.

简而言之, 应用 FutureTask 实现线程的时候实质上还是重写了 Runnable 的 run(), 不过区别在于 FutureTask 能够拿到工作的返回后果, 而继承 Thread 类和实现 Runnable 接口都无奈拿到返回后果.

 start办法的执行流程是什么?

  1. 查看线程状态, 只有 NEW 状态下的线程能力持续, 否则会抛出 IllegalThreadStateException (在运行中或者已完结的线程,都不能再次启动,详见CantStartTwice10 类)
  2. 被退出线程组
  3. 调用 start0() 办法启动线程

留神点:

start 办法是被 synchronized 润饰的办法, 能够保障线程平安;

由 JVM 创立的 main 办法线程和 system 组线程, 并不会通过 start 来启动.

 为何调用 start(), 而不是间接调用 run() 呢?

调用 start() 之后, 一个线程才会被退出线程组, 进入生命周期. 如果仅仅从 main 中间接调用 run(), 那只是从 main 线程执行了一次 run() 办法, 并无新线程产生.

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理