又是javaXX浅谈,可能有人会问了你天天浅谈浅谈的,就不能深谈?说实话技术这块随便拎个看似不起眼的点扩展开都能详细的让你怀疑人生,所以短短这么一篇博客不敢随便说深聊,你说深聊结果来个说浅谈的分分钟把你虐死,让你怀疑小学是不是没学好深和浅的概念。所以浅谈浅谈,谈个基础,碰到问题知道从哪方面思考,从哪方面找知识点就足够了。废话不多说,进入正题,今天的大咖:多线程!多线程是个啥?打开手机用app听个歌,发现听歌的同时还有歌词显示,可以边听边收藏,甚至还可以边听边评论哪个歌手唱的跟鸭子一样难听,这app的多面手就是靠的多线程,可以从这里看多线程的机制就是指可以同时运行多个程序块,让我们可以同时干好几样事情,而不用干完一样等下一样。注意这里的同时二字,AB不是按照某一固定顺序而是A先B后,或者B先A后,或者一直都A,一直都B,具体哪种执行按照哪个线程对象抢到了cpu资源而定。
跟我们一年有春夏秋冬不同的季节一样,线程也有他的状态,创建→就绪→运行→挂起→死亡。当我们new一个线程对象后,等于线程有了内存空间和其他资源;当这个对象去调用start()方法后就等于是启动线程了,线程进入队列排队等待cpu资源;一旦资源到手,线程就自动调用run()方法跑起来了,这个时候线程开始真正给我们干活了;如果此时你去上厕所了想让这件事停下来也可以挂起线程,让线程sleep()或者wait()你上完厕所回来;当任务执行完毕,线程就说拜拜了。
那代码里是怎么实现多线程的?机器是怎么拥有这种同时干多件事的能力的?有两种方式实现,一种是继承Thread类,一种是继承Runnable接口,来看下下面的代码:
第一种方式:
class 类名称 extends Thread{
属性;
构造函数;
public void run(){
要干什么事的代码在这里复制粘贴
}
public class Demo1{
main方法中{
类实例化对象m1;
类实例化对象m2;
m1.start();//启动线程,只能用Thread类中的start()方法,但实际调用的还是我们自己写的run()方法的主体
m2.start();
}
}
第二种方式:
class 类名称 implements Runnable{
属性;
构造函数;
public void run(){
要干什么事的代码在这里复制粘贴
}
public class Demo2{
main方法中{
类实例化对象m1;
类实例化对象m2;
Thread t1=new Thread(m1);
Thread t2=new Thread(m2);
t1.start();//启动线程,虽然用的是Thread类中的start()方法,但实际调用的还是我们自己写的run()方法的主体
t2.start();
}
}
可以看出继承Runnable多了两行代码,心里小声嘀咕着Runnable写着更麻烦,我要用Thread的方式。别,Runnable要喊委屈了,他在多线程中有个与Thread不同的地方就是资源共享。虽然同一个线程对象只能启动一次,但如果每次start我都传相同的线程对象给Thread的不同对象不就实现了同一个线程对象启动多次的目的了。别以为资源共享就解决事情了,看看市面上被到处乱扔的共享单车,想到工作日的早晨走了一条街都找不到一辆好车的窘境,这都是资源共享同时附带的问题。
就拿去上公共厕所来说,你在上厕所的同时仅剩的一点厕纸给另外一个上厕所的人拿走了,而你这个时候又不知道,于是当你再去抽纸的时候尴尬就发生了;碰到这种尴尬的问题,有个办法就在你上厕所sleep()的同时你把公共厕所里的厕纸用封条贴上说明你要先用,其他人谁先拿就谁罚款(虽然实际当中作为公共资源是不能这样使用的),那就没人敢拿了,也就确保你上完厕所后能拿到纸,这个办法就是synchronized同步。但如果又涉及到第三方,你规定张三先用,而张三并不知道你说让他先用,他心里觉得要让你先用,两人在这种互相谦让的状态下就导致了资源浪费了,类似递归调用的死循环。那这种情况怎么办,就是明着在你拿厕纸的时候告诉张三我等你wait()拿完再拿,你拿完了喊下我notify()我再拿,你和张三两个都挑明了那就啥事都没有了。
多线程是个提升效率的好工具,但同时也容易引发性能问题的和数据混乱问题,这么高深的学问那也不能一口气吃成个傻胖子,多了解才能发挥长处,关于多线程今天就写到这里,期待后面继续!