Java并发编程之实现多线程的方法

实现线程的正确方法:2种

  • 实现Runnable接口创建线程
/**
 * 实现Runnable接口创建线程
 */
public class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口创建线程");
    }

    public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }
}
  • 继承Thread类创建线程
/**
 * 继承Thread类创建线程
 */
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread类创建线程");
    }

    public static void main(String[] args) {
        new Thread(new MyThread()).start();
    }
}

实现Runnable接口创建线程的方法更好

  1. 解耦:具体执行的任务,也就是run方法应该与线程的创建、运行、销毁是不相关的;
  2. 资源节约:如果用继承Thread这种方法,每次新建个任务只能新建个线程,线程的创建、运行和销毁的消耗是比较大的;如果使用实现Runnable接口的方法,后续我们可以使用线程池等工具,不用再去新建线程,带来的损耗会大大降低;
  3. 扩展性:Java是不支持多继承的,如果采用继承Thread类的方法,那么子类就无法再去继承其他类,大大的限制了子类的扩展性

两种方法的本质对比

让我们看看Thread类里面的run()是怎么写的:

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
  • 代码非常的简单,意思是有Runnable类型的target对象,那么就执行target对象里面的run();
  • 使用实现Runnable接口方法时,因为传入了Runnable对象,所以新建线程对象里面执行了target.run();
  • 使用继承Thread类方法时,因为子类重写了Thread类的run(),所以最终执行子类的run();

同时使用两种方法会怎么样?

public class MyThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("实现Runnable接口创建线程");
            }
        }) {
            @Override
            public void run() {
                System.out.println("继承Thread类创建线程");
            }
        }.start();
    }
}
继承Thread类创建线程

上述代码虽然传入了Runnable对象,但是子类重写了Thread类的run(),导致该方法不再调用target.run(),所以同时使用两种方法,只有继承Thread类方法才生效!

最精准的描述

  • 通常可以分为两类:实现Runnable接口、继承Thread类重写run()
  • 准确的讲,创建线程只有一种方式:创建Thread对象,而在Thread类里面,实现run()有两种不同的情况:
    1. 实现Runnable接口,并把实例对象传给Thread类去执行run();
    2. 继承Thread类,直接重写了Thread类的run();

典型的错误观点

  • “线程池创建线程”
public class MyThread {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}
pool-1-thread-14
pool-1-thread-9
pool-1-thread-8
pool-1-thread-13
pool-1-thread-15
pool-1-thread-16
......

结果表明,线程池确实创建了新的线程,但是这只是表现,其内部是通过实现ThreadFactory接口来创建线程的,如下所示:

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}
  • “通过Callable和FutureTask创建线程,也算一种新的创建线程的方式”
public class CallableAndFutureTask {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                return Thread.currentThread().getName();
            }
        });
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}
Thread-0

实现Callable接口并创建FutureTask对象的方法确实也可以创建线程,但是和线程池一样,这只是对创建Thread对象的一种包装,如下图所示:

public class FutureTask<V> implements RunnableFuture<V>
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实现了RunnableFuture接口,而RunnableFuture继承与Runnable接口,根据这种传递关系,相当于FutureTask实现了Runnable接口,在实现的run()方法中调用了Callable接口的call方法来获取返回值,并把返回值set到result中,最后通过get()获取到返回值,如下图所示:

    public void run() {
        .......
          Callable<V> c = callable;
	      result = c.call();
          ran = true;
          if (ran) set(result);
          }
        ......
    }

以上两种说法,只是对于实现Runnable接口和继承Thread类方法的封装,本质上不算新的方法!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章