【Java并发编程】刨根问底搞懂创建线程到底有几种方法?

苡仁 2020年03月13日 104次浏览

Thread源码分析

    /* What will be run. */
    private Runnable target;

		public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

		private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
		
		private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        ......
        
        this.target = target;
        
      	......
    }

		@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
  • 我们可以看到平时我们通过实现Runnable接口和继承Thread来重写run方法,最终归结到了run方法的调用上。一个是重写,一个是调用接口的方法。

Oracle官方文档对创建线程的说明

  • Java SE 8 API文档: https://docs.oracle.com/javase/8/docs/api/

    请查看java.lang.Thread的类说明文档。

    1. 将类继承Thread类重写run方法

    官方原话:There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread.

    /**
     * 实现线程的第一个方式 继承Thread
     * @author yiren
     */
    public class MyThread extends Thread {
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Thread running...");
        }
    
        public static void main(String[] args) throws IOException {
            new MyThread().start();
            System.in.read();
        }
    }
    
    1. 实现Runnable接口

    官方原话:The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method.

/**
 * 实现线程的第二个方式 实现Runnable接口
 * @author yiren
 */
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Runnable running...");
    }

    public static void main(String[] args) throws IOException {
        new Thread(new MyRunnable()).start();
        System.in.read();
    }
}

Runnable的优点

  1. 业务代码与线程类创建启动等逻辑解耦。
    • 依赖倒置原则:抽象不应该依赖具体,具体应该依赖抽象
  2. Runnable可复用,Thread则需要每次创建。
  3. 类可以实现多个接口,而不可以继承多个对象。所以接口更好
  • 如果两种方式都用会有什么效果呢?

    /**
     * @author yiren
     */
    public class MyThreadAndRunnable {
        public static void main(String[] args) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " runnable running...");
                }
            }
            ) {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " thread running...");
                }
            };
    
            // 这个地方应该是执行重写Thread类的run方法中的逻辑!
            thread.start();
        }
    }
    
    • 很明显,上面说了不重写Threadrun()方法就是调用target.run(),如果重写那也就没有调用target.run()了。

归根结底

  • 创建线程只有一种方式,就是创建Thread类的对象,而构建一个线程的方式则有多种:比如创建线程类、实现Runnable接口、创建线程池、FutureTask等等。

  • 线程池创建线程:实际是由默认的工厂代为创建Thread类来实现。

    // Executors中的DefaultThreadFactory 
    static class DefaultThreadFactory implements ThreadFactory {
            private static final AtomicInteger poolNumber = new AtomicInteger(1);
            private final ThreadGroup group;
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            private final String namePrefix;
    
            DefaultThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                                      Thread.currentThread().getThreadGroup();
                namePrefix = "pool-" +
                              poolNumber.getAndIncrement() +
                             "-thread-";
            }
    
            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;
            }
        }
    
    • 由上newThread()方法可知,即使是线程池,本质上还是使用Thread的创建线程。
  • Callable和FutureTask创建线程,本质其实也是Thread

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

  • 定时器Timer:它的TimerTask其实也是实现了Runnable接口,可以看下TimerTask这个抽象类

    
    /**
     * @author yiren
     */
    public class TimerExample {
        public static void main(String[] args) {
            Timer timer = new Timer();
            // 每隔1s打印下自己的名字
            timer.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " timer running...");
                }
            }, 1000, 1000);
        }
    }
    

  • 文章内容来源:
  • 《Java多线程核心》、JDK1.8版本源码、Oracle官方文档、慕课网悟空JUC课程