携手创作,共同成长!这是我参与「掘金日新计划 · 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();
这样有什么好处呢?就是把任务和线程分离,好控制。然后我们打印出来看一下:
我们可以看到我们没有命名,这个线程自动给我们命名了,那这名字是哪里来的呢?我们访问源码看一下,是不是这个方法,点进去看:
线程初始化number,初始化就是0叭,然后调用一个加1很简单。
建议还是起个线程名字,这样遇到问题好排查问题。
继承和实现的区别
实际上继承Thread类呢,是重写了Thread里面的run方法,是把这个run方法重写了。
所以,会发生你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:主线程。