【线程基础】实现多线程

邓敏 2021年12月07日 86次浏览

方法一 继承Thread类

public class Thread01 extends Thread{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" is running...");
    }

    public static void main(String[] args) {
        Thread01 thread01 = new Thread01();
        thread01.start();
    }
}

方法二 实现Runnable接口

public class Thread02 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+" is running...");
    }

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

方法三 通过Callable和FutureTask创建线程

public class Thread03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> callable = new Callback<String>();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }

    static class Callback<String> implements Callable<String>{
        @Override
        public String call() throws Exception {
            System.out.println("exec one times...");
            TimeUnit.SECONDS.sleep(2);
            System.out.println("sleep tow seconds");
            return (String) (Thread.currentThread().getName() + " is pass through callable accomplish");
        }
    }
}

方法四 通过线程池创建线程

public class Thread04 {

    static class RunnableTask implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" is running...");
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.execute(new RunnableTask());
        executorService.shutdown();
    }
}

方法五 定时器Timer

public class Thread05 extends TimerTask {
    @Override
    public void run() {
        System.out.println(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)+":"+Thread.currentThread().getName()+" is running...");
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        //隔一秒后启动,之后每隔两秒启动一次。
        timer.schedule(new Thread05(), 1000,2000);
    }
}

方法六 匿名内部类创建线程

public class Thread06 {

    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            System.out.println(Thread.currentThread().getName()+" is running...");
        });
        thread.start();
    }
}

总结

在上面六种方法中虽然都能创建线程,但其实他们每种方法在内部,其实都是通过实现Runnable接口来实现的

请看下面几张图片
Thread
image.png

TimerTask
image.png

FutureTask
image.png

ExecutorService
image.png

可以看到,不管创建线程的方式千遍万化,他的底层始终都逃不过实现Runnable接口,所以归根结底,创建线程的方式只有一种。

单如果仔细想一下,为什么Java都创建线程的方式都做成实现Runnable接口来看,相比继承Thread接口,实现Runnable接口有什么好处。

  • 其实在Java中,只允许单继承,但是可以实现多接口。对于这个特性其实就体现出了线程去实现Runnable接口的灵活性。
  • 另外,在某些情况下通过实现Runnable的方式可以提高性能。使用继承Thread的方式,没执行一次任务,都需要新建一个独立的线程,执行完任务后的线程周到生命周期的尽头被销毁,如果还想执行这个任务,就必须要再新建一个继承Thread类的线程。如果用实现Runnable接口的方式,就可以直接把任务直接传入线程池,使用一些固定的线程来完成任务,不需要新建和销毁线程。这样就大大的降低了性能开销。

总结一下,如果有面试官问我们一共有几种创建线程的方式,就跟他说只有一种,那就是通过实现Runnable接口的方式来创建线程。