多线程并发的实现区别

809 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情 这篇文章我们来详细的了解一下多线程并发协作的问题,我们单拿消费者举例,上一篇我们实现多线程是使用这个继承的方式,但java是不支持多继承的,所以我们选择用runnable来改造线程。

消费者

我们先制定任务

public class Consumer implements Runnable{
    private  static int fileCount = 24;
    private  static int fileIndex = 0;
    @Override
    public void run() {
        while (fileIndex<=fileCount){
            fileCount--;
            System.out.println("导入多少个"+fileCount+"文件");
        }
    }
}

然后我们来new线程

Consumer consumer = new Consumer();
Thread thread = new Thread(consumer);
Thread thread1 = new Thread(consumer);
Thread thread3 = new Thread(consumer);
thread.start();
thread1.start();
thread3.start();

这样有什么好处呢?就是把任务和线程分离,好控制。然后我们打印出来看一下:

image.png

我们可以看到我们没有命名,这个线程自动给我们命名了,那这名字是哪里来的呢?我们访问源码看一下,是不是这个方法,点进去看:

image.png

线程初始化number,初始化就是0叭,然后调用一个加1很简单。 image.png

建议还是起个线程名字,这样遇到问题好排查问题。

image.png

继承和实现的区别

实际上继承Thread类呢,是重写了Thread里面的run方法,是把这个run方法重写了。

image.png

所以,会发生你new一个线程类,执行的都会是一个新的方法。

如果实现runnable接口呢?你重写的是target里面的方法,所以你不管造多少个线程,运行的方法都是一个target里面的方法,这就是区别。

那操作系统调用的是哪个run呢?是只认识Thread里面的run方法,

拓展

这篇文章我们再带大家了解一下虚拟机创建的几个额外的线程,。

Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
allStackTraces.keySet().stream().forEach(thread -> System.out.println(thread.getName()));

执行上面代码,会看到如下结果:

  • Finalizer:垃圾回收线程。
  • Signal Dispatcher:信号分发线程,将监听器监听到的事件,分发给不同的模块,如果你学过springmvc对这个线程应该不陌生。
  • Reference Handler:引用处理线程
  • Monitor Ctrl-Break:死锁检测。
  • Attach Listener:事件监听器
  • main:主线程。