多线程的前世今生

Posted Spring技术栈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程的前世今生相关的知识,希望对你有一定的参考价值。

现在出来找工作,不管面试BAT,还是其他中小企业,好像不聊聊JDK源码,不探讨下框架原理就很没有档次,没有共同话题了一样,笔者也是几经折腾,在这条路上……额 被虐过吧 所以,静下心,我们再聊多线程


1.多线程有什么用?


面试官:有没有用过多线程?用多线程解决什么问题?为什么要用多线程?面试者或许有准备,知道一些,当问题循序渐进,问到并发线程数,线程池,线程安全…… 面试者一脸懵逼,于是时间静止没法聊了,其实说白了是知识系统性的问题,需要从源头出发,了解多线程的来源,发展,技术点,以及使用场景等等,下面笔者谈谈自己的理解

  • 提高CPU使用率

    远古时代CPU都是单核的,单核的,就好像一个工厂,只有一条生产线,所有的生产任务必须排队,一个任务执行完了另一个任务顶上。现在我们知道CPU有时间片的概念,那么生产任务不需要排队,可以是排成一排,每个任务执行完一个时间片后交出CPU使用权,轮训使用,看上去好像大家齐头并进一样,但实际上那是一个假象,仍然是单核处理,就是一个生产线。后来到了唐朝,我们知道就强大了嘛,引进了新的生产线,搞出来多核,这就厉害了,可以多条生产线并行生产了。所以呢,生产任务安排的时候,要切实考虑到硬件资源,让生产线都动起来,发挥它的存在价值

  • 模型思维的转变

  • 笔者认为这个更难能可贵,CPU多核的出现,改变了应用设计模式的转变,这是一个跳跃,从单一的串行模式(或者并行模式,注意,不是并发)变成了并发模式,使得在建模的时候也更加方便,可以有更多的可能性


2.怎么创建多线程?


  • 继承 Thread类或者实现Runnable接口

    多线程的前世今生这里我把这两种实现方法放在一起是因为他两同出一源,Thread本身就是后者的一种实现,提供了对Runnable接口中run方法的默认实现,但是笔者推荐大家使用后者Runnable接口,原因是Java本身单继承,同时实现接口使得代码更具有拓展性,也是设计模式原则之一:面向接口编程


  • 实现Callable接口

    多线程的前世今生

    与Runnable比较,大家很容易发现几个不同点,首先呢,Callable有泛型结果集返回,可以被主线程调用,而Runnable是void;其次,Runnable线程内允许异常抛出,Runnable也不支持。


调用start()与调用run()有什么区别?

线程我们会创建了,如果是基于Thread类或者实现Runnable接口,我们会通过调用start是的该线程处于就绪状态,等待CPU的调度,但是我们知道里面真的还有一个run方法,他与start有什么区别呢?区别是run是对象内部的方法,调用该方法相当于主线程内的简单方法调用,与多线程无关,而start则会做深入的处理,开启一个新的线程,感兴趣的同学可以了解下


3.多线程使用场景呢?


  • 充分利用CPU,发挥多核优势,在短时间窗内处理完一个大的任务多线程的前世今生

  • 业务逻辑解耦。没错,就是业务逻辑解耦,比如说网站注册流程,当用户成功注册后,我们希望可以给用户下发欢迎邮件,但实际上你会发现,用户此时已经注册成功了,邮件下发用户并不care,此时如果下发邮件异常……就悲剧了!这个时候我们可以再起一个异步线程,用户注册完毕即返回,发邮件的事情,让别人去嗨吧(当然只是个例子,实际上发邮件基本都是基于消息中间件的)

  • 异步处理,结果回调。一般常用的两种,基于Runnable的话,相当于提交任务的时候,附带一个回调对象,异步逻辑结束后,执行回调对象的内部方法,但是这种场景下,主线程与回调线程分属两个线程,主线程不能获取到异步线程的结果集,真正的异步处理,谁跟谁没关系;当主线程启动异步线程后,我们希望主线程在后面的执行中可以获取到异步线程的结果的时候就可以实现Callable接口,它会返回一个泛型Future对象,通过提供的get方式得到异步线程的返回结果,是不是很赞?

  • ……


4.线程可以无限开吗?


  • 硬件限制。既然多线程这么牛逼,那我开1000个线程好了,一定很快吧?其实不然,首先呢,了解JVM的同学知道线程是有私有内存的,也就是说,每开启一个线程,都要为其分配一定大小的内存空间,我们知道机器的内存是很宝贵的,所以线程开多少个合适受机器硬件的限制。

  • 业务类型限制。我们一直说多线程是为了提高CPU的使用率,也就是说当CPU 100%时间在做事的时候是最合适的,也就达到了最高的利用率。那假设我们的业务是IO密集型业务,CPU比较空闲,经常处于等待状态(让我歇一会怎么了?多线程的前世今生),显然是不合适的,这个时候可以创建较多的线程数让CPU动起来;假设业务类型的计算密集型的,CPU已经忙的团团转,汗流浃背了(这日子没法过了多线程的前世今生),你创建再多的线程也只会堆积起来,排队处理,再者我们知道线程过多时,频繁的上线文切换反而会降低CPU效率,所以大家根据自己业务适量创建为好


5.线程池是个啥?


创建了一大堆线程是不是很爽?这个时候我们发现频繁的创建线程、销毁线程也很消耗资源,商机啊,那我是不是可以搞一个商店(线程池),提前初始化一些线程,任务来的时候,直接从我这里租一个线程去执行业务,执行完了再给我还回来,既可以管理异步任务,也提高了资源利用,多好?关于深入的线程池讲解,请回看


多线程的前世今生
多线程的前世今生

长按关注

关注


以上是关于多线程的前世今生的主要内容,如果未能解决你的问题,请参考以下文章

线程的前世今生

线程池的前世今生

追溯volatile的前世今生

Redis线程模型的前世今生

async&await的前世今生

Redis 线程模型的前世今生