Java创建多线程的两种常用方式浅析

11次阅读

共计 1972 个字符,预计需要花费 5 分钟才能阅读完成。

Java 实现多线程有两种常用方法:继承 Thread 类和实现 Runable 接口。下面学习下两种方法的源码。
1. 继承 Thread 类
这种方法实现多线程示例如下:

class MyThread extends Thread{
    @Override
    public void run(){System.out.println("Hello thread");
    }
}
public class ThreadPool {public static void main(String args[]){MyThread myThread = new MyThread();
        myThread.start();}
}

可以看出,这种方法先是继承 Thread 类,重写 Thread 类的 run 方法,要使用多线程时再调用 start 方法。之前一直有一个疑问,为什么重写的是 run 方法,而调用的是 start 方法呢。run 方法和 start 方法有什么关系呢?今天好好的研究下它们的关系。
点开 start 方法的源码,可以看到它的实现(为了看起来简洁,去掉注释了):

 public synchronized void start() {if (threadStatus != 0)
            throw new IllegalThreadStateException();
        boolean started = false;
        try {start0();
            started = true;
        } finally {
            try {if (!started) {group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {}}
    }

从上面代码可以看出,start 方法调用了 start0 方法,而点开 start0 可以看到下面这行代码

  private native void start0();

start0 方法被 native 修饰,是本地方法。查阅资料后发现 JDK 官方文档在 start 方法有这样的注释:
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread. 从这句话可以知道 start 方法调用 start0 方法,而 start0 则会通过 JVM 调用 run 方法。过程可以用下图表示:

可以看出这里使用了设计模式中的模板方法模式。模板方法模式定义:是在父类中定义算法的骨架,把具体实延迟到子类中去,可以在不改变一个算法的结构时可重定义该算法的某些步骤。真正实现逻辑的是 run 方法,因此使用者只需要重写 run 方法。

2. 实现 Runnable 接口
通过实现 Runnable 接口实现多线程代码示例如下:

class MyRunnable implements Runnable{
    @Override
    public void run() {System.out.println("Hello Runnable!");
    }
}
public class Demo {public static void main(String[] args) {new Thread(new MyRunnable()).start();}
}

先重写了 Runnable 接口的 run 方法,然后将 Runnable 实例用作构造 Thread 的参数。打开 run 方法的源码可以看到它的实现如下:

  /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {if (target != null) {target.run();
        }
    }

由上面的注释可以知道如果构造 Thread 时传递了 Runnable,则会执行 runnable 的 run 方法。否则需要重写 Thread 类的 run 方法。

通过上面的分析可知,无论还是实现 Runnable 接口还是继承 Thread 类,其实底层都是重写 Thread 的 run 方法,只不过第一种是直接重写 Thread 类的 run 方法的实现,第二种是在另一个类中重写 run 方法,再传递到 Thread 中的 run 方法执行,也相当于重写了 run 方法(通过调用其他类中的 run 方法)。

像第二种方式其实用到了策略模式,把具体实现策略分离出来,再在执行的类中调用策略。

正文完
 0