温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

实现Java线程的方法有哪些

发布时间:2021-12-31 09:22:54 来源:亿速云 阅读:108 作者:iii 栏目:大数据

这篇文章主要介绍“实现Java线程的方法有哪些”,在日常操作中,相信很多人在实现Java线程的方法有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”实现Java线程的方法有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

搞懂两件事: 为什么说本质上只有一种实现线程的方式? 实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?

一:常见的实现线程的方式

1:实现Runable接口

public class RunnableThread  implements Runnable{
    @Override
    public void run() {
        System.out.println("用实现Runnable接口实现线程");
    }
    public static void main(String[] args) {
        Thread thread=new Thread(new RunnableThread());
        thread.start();
    }
}

如代码所示,首先通过 RunnableThread 类实现 Runnable 接口,然后重写 run() 方法,之后只需要把这个实现了 run() 方法的实例传到 Thread 类中就可以实现多线程。

2:继承Thread类

public class ExtendsThread extends Thread{
    @Override
    public void run() {
        System.out.println("用继承Thread类实现线程");
    }
}

与第 1 种方式不同的是它没有实现接口,而是继承 Thread 类,并重写了其中的 run() 方法。

3:线程池创建线程

下面是线程池中的源码,来看看线程池是怎么实现线程的:

    /**
     * The default thread factory
     */
    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;
        }
    }

对于线程池而言,本质上是通过线程工厂创建线程的,默认采用 DefaultThreadFactory ,它会给线程池创建的线程设置一些默认值,比如:线程的名字、是否是守护线程,以及线程的优先级等。但是最终都是通过new Thread()创建线程的,只是参数多了些而已

4:有返回值的 Callable 创建线程

public class CallableTask implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return new Random().nextInt();
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Future<Integer> future = executorService.submit(new CallableTask());
        System.out.println(future.get());
        System.out.println(future.isDone());
        System.out.println(future.isCancelled());
    }
}

Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,如代码所示,实现了 Callable 接口,并且给它的泛型设置成 Integer,然后它会返回一个随机数。

但是,无论是 Callable 还是 FutureTask,它们首先和 Runnable 一样,都是一个任务,是需要被执行的,而不是说它们本身就是线程。它们可以放到线程池中执行,如代码所示, submit() 方法把任务放到线程池中,并由线程池创建线程,不管用什么方法,最终都是靠线程来执行的,而子线程的创建方式仍脱离不了最开始讲的两种基本方式,也就是实现 Runnable 接口和继承 Thread 类

5:匿名内部类、lambda表达式创建线程

    public static void innerClassThread(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类创建线程");
            }
        }).start();
    }

    public static void lambdaThread(){
       new Thread(()->{
           System.out.println("lambda表达式创建线程");
       }).start();
    }

实际上,匿名内部类或 lambda 表达式创建线程,它们仅仅是在语法层面上实现了线程,并不能把它归结于实现多线程的方式,如匿名内部类实现线程的代码所示,它仅仅是用一个匿名内部类把需要传入的 Runnable 给实例出来。

二:为什么说本质上只有一种实现线程的方式?

了解了上面的几种创建线程的方式后,我们发现所有其他创建线程的方式都仅仅是在 new Thread() 外做了一层封装而已;不同点仅仅在于实现线程运行内容的不同。

  1. 实现线程执行的内容,通过实现 Runnable 接口的方式

启动线程需要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,Thread中的run()方法源码如下:

    /* What will be run. */
    private Runnable target;
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
  • target其实就是一个Runnable,实际线程执行的内容就是实现了Runnable的接口中的run方法。

  1. 实现线程执行的内容,继承 Thread 类重写 run() 方法的方式

  • 启动线程需要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,但是此时的run()方法,因为继承的原因,此时已被子类重写了,所以此时线程执行的内容是子类的run方法

运行内容主要来自于两个地方,要么来自于 target,要么来自于子类重写的 run() 方法;因此可以这样总结:本质上,实现线程只有一种方式new Thread(),而要想实现线程执行的内容,却有两种方式,要么通过实现 Runnable 接口的方式,要么继承 Thread 类重写 run() 方法的方式,把我们想要执行的代码传入,让线程去执行

三:实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?

  1. Runnable 里只有一个 run() 方法,它定义了需要执行的内容,在这种情况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,职责分明。

  2. java单继承的原因,类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,限制了代码未来的可拓展性。

  3. 在某些情况下可以提高性能,如果使用继承 Thread 类方式,每次执行一次任务,都需要新建一个独立的线程;使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。

到此,关于“实现Java线程的方法有哪些”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI