【多线程】实现多线程的正确姿势

关于网上Java多线程实现的方式

在这里插入图片描述

Oracle的官方文档

  • 两种

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread.

The other way to create a thread is to declare a class that implements the Runnableinterface. That class then implements the run method.

  • 继承Thread
public class ThreadStyle extends Thread{
    @Override
    public void run() {
        System.out.println("thread name is " + Thread.currentThread().getName() + ",thread id="  + Thread.currentThread().getId());
    }

    public static void main(String[] args) {
        new ThreadStyle().start();
    }
}
  • 实现Runnable接口
public class RunnableStyle implements Runnable{

    @Override
    public void run() {

        System.out.println("thread name is " + Thread.currentThread().getName() + ",thread id="  + Thread.currentThread().getId());

    }

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

RunnableThread的实现对比

结论: Runnable更好

  • 从代码架构角度考虑:具体执行的任务(run()方法)应该和线程的创建、运行机制(Thread类)是解耦的。
  • 从资源的角度:继承Thread每次需要新建任务只能新建一个独立的线程,而新建一个独立的线程对资源的损耗是比较大的。如果使用Runnable就可以使用线程池。
  • 从继承的角度:继承Thread类Java只支持单继承,导致该类无法继承其它类,大大限制了可扩展性。

RunnableThread的本质

public class Thread implements Runnable {
    private Runnable target;
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}
  • Runnable:最终调用target.run();
  • Thread:子类重新父类run();

同时使用RunnableThread会怎么样。

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("I am from Runnable.");
            }
        }){
            @Override
            public void run() {
                System.out.println("I am from Thread.");
            }
        }.start();
    }

输出:

I am from Thread.

从面向对象的思想去考虑: 内部类重写run方法,相当于重写Thread类的run方法;创建Thread的同时传入了Runnable,根据Java语法规定,如果同时传入Runnable,又重写了Thread类的run方法,由于Threadrun方法已经被重写,所以重写后的代码不再包含以下代码:

    if (target != null) {
        target.run();
    }

也就是说传入的Runnable不会再执行。

实现线程的方式总结

  • 通常可以分为两类,来源Oracle官方。
  • 准确来讲,创建线程只有一种方式那就是构造Thread类,而实现线程的执行单元有两种方式。
  • 方法一: 实现Runnable接口的run方法,并把Runnable实例传给Thread类。
  • 方法二: 重写Threadrun方法(继承Thread类)。

典型错误观点分析

  • " 线程池创建线程也算是一种新建现成的方式"
   public static void main(String[] args) {
       ExecutorService executorService = Executors.newCachedThreadPool();

       for (int i = 0; i < 100; i++) {

           executorService.submit(new Task());
       }
       executorService.shutdown();
   }
class Task implements Runnable{

   @Override
   public void run() {
       try {
           TimeUnit.MILLISECONDS.sleep(500);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("current thread name is " + Thread.currentThread().getName());
    }
}

线程池是由DefaultThreadFactory创建的,而创建线程是使用newThread,查看源码

Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);

发现线程池还是使用Thread来创建的。

  • “通过CallableFutureTask创建线程,也算是一种新建线程的方式”

Callable结构图
在这里插入图片描述
Future结构图
在这里插入图片描述

  • 定时器
    public static void main(String[] args) {

        Timer timer = new Timer();

        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("current thread name is " + Thread.currentThread().getName());
            }
        },0, 1000);
    }

查看Timer源码:

public Timer(String name) {
   thread.setName(name);
   thread.start();
}
private final TimerThread thread = new TimerThread(queue);
class TimerThread extends Thread {}

查看源码可知TimerThread继承于Thread

  • 匿名内部类
  public static void main(String[] args) {

       new Thread(){
           @Override
           public void run() {
               System.out.println("current thread name is " + Thread.currentThread().getName());
           }
       }.start();

       new Thread(new Runnable() {
           @Override
           public void run() {
               System.out.println("current thread name is " + Thread.currentThread().getName());
           }
       }).start();
   }
  • Lambda表达式
new Thread(() -> System.out.println("current thread name is " + Thread.currentThread().getName())).start();
  • 多线程的实现方式在代码中写法千变万化,但其本质万变不离其宗。

实现多线程-常见面试问题

  • 有多少种实现线程的方法?思路有5点
    1.从不同的角度看,会有不同的答案。
    2.典型答案是两种。
    3.从原理来看,两种本质是一样的。
    4.具体展开说其他方式。
    5.结论。
    
  • 实现Runnale接口和继承Thread类哪种方式更好?
    1.接口(从代码架构角度)。
    2.资源(新建线程的损耗)。
    3.继承(Java不支持双继承)
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值