在Java中使用多线程并探究线程的启动方法

使用多线程

单线程同步,CPU利用率低;而线程异步,运行效率高。
1. 实现 Runnable 接口

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

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

Runnable只是一个接口,它里面只有一个run()方法,没有start()方法,可以使用Thread类来启动。由此可见Runnable接口,并不能代表一个线程,Runnable接口和线程是两个不同的概念。

实现Runnable接口所具有的优势:

1、解决Java只能单继承的问题
2、适合多线程处理同一资源
3、代码可以被多线程共享,数据独立,很容易实现资源共享

2. 继承 Thread 类,并且重写 run 方法

class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
     PrimeThread p = new PrimeThread(143);
     p.start();

Thread类如下所示

public class Thread extends Object implements Runnable

Thread类实现了Runnable接口,它们之间具有多态关系。使用继承Thread类的方式创建新线程时,最大的局限就是不支持多继承,我们可以用实现Runnable接口的方式,一边实现一边继承。这两种方式创建的线程在工作时的性质是一样的,没有本质区别。

Thread中start()方法与run()方法的区别与联系

一个小实验

MyRunnable.java

public class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name){
        this.name = name;
    }

    @Override
    public void run(){
        for(int i=0;i<20;i++)
            System.out.println(name + i);
    }
}

Main.java

public class Main {

    public static void main(String[] args) {
        MyRunnable[] myRunnables = new MyRunnable[3];
        myRunnables[0] = new MyRunnable("A");
        myRunnables[1] = new MyRunnable("B");
        myRunnables[2] = new MyRunnable("C");
        new Thread(myRunnables[0]).start();
        new Thread(myRunnables[1]).start();
        new Thread(myRunnables[2]).start();

//        new Thread(myRunnables[0]).run();
//        new Thread(myRunnables[1]).run();
//        new Thread(myRunnables[2]).run();
    }
}

第一次将所有的run方法注释掉,运行结果如下:

A0 A1 B0 A2 A3 B1 A4 B2 B3 B4 B5 B6 B7 A5 B8 B9 C0 A6 A7 A8 B10 B11 B12 A9 B13 B14 
B15 B16 B17 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 C14 C15 C16 C17 C18 C19 B18 
A10 B19 A11 A12 A13 A14 A15 A16 A17 A18 A19

三个线程同时运行,切换输出,每一次结果都是随机的。

第二次将所有的start方法注释掉,运行结果如下:

A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 A16 A17 A18 A19 B0 B1 B2 B3 
B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 C0 C1 C2 C3 C4 C5 C6 C7 
C8 C9 C10 C11 C12 C13 C14 C15 C16 C17 C18 C19

结果每次都一样,相当于主线程依次调用run方法,按顺序输出。

查看源码来验证猜想

    public synchronized void start() {
        /*当一个线程启动的时候,threadStatus被设置为0,如果不为0,
         *则抛出IllegalThreadStateException异常*/
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /*把其添加到线程组中*/
        group.add(this);

        boolean started = false;
        /*尝试调用start0方法*/
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

        /*start0方法是私有的native方法,Native Method是一个
         *java调用非java代码的接口,猜测是用C实现的*/
    private native void start0();

start()方法由于调用了start0方法可以实现多线程。

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

如果target不为空,则调用target的run()方法,target就是一个Runnable接口,正如上面代码中new Thread的部分,其实我们就是在实现它的run()方法。所以如果直接调用run,就和调用一个普通的方法没什么区别,是不会创建新的线程的,因为压根就没执行start0方法。

综上可知,我们的猜想是正确的,也符合源代码的设计,在用到多线程时,要用start方法来启动,切勿使用run方法。


参考资料:

《Java多线程编程核心技术》 高洪岩
JDK 8 源代码
Java™ Platform Standard Ed. 8

附:

程序是一组指示计算机或其他具有消息处理能力装置每一步动作的指令。(静态)
进程是程序的一次的执行活动,是计算机中已运行程序的实体。进程是线程的容器。(动态)
线程是在进程中独立运行的子任务,一个进程可以拥有多个线程,一个进程在运行时至少会有一个线程在运行。

猜你喜欢

转载自blog.csdn.net/ldx19980108/article/details/81187242