从今天起,我将整理多线程的笔记,从浅显到深入去剖析多线程的面纱。当然这也是各大面试中的重点,因为笔者在各个大厂面试中吃过多线程的亏,所以痛并思痛决定在多线程上面下一点功夫。
- 为什么要使用多线程?
- 创建线程的几种方式 ?
- 线程的各种状态?
- 线程状态之间的转换?
- 三个线程,怎么保证线程2 在线程1 之后执行,线程3 在线程2 之后执行
其实任何业务都是可以在一条线程下执行的,比如有两块业务代码,第二块业务代码不需要依赖第一个业务,但是第二个业务代码块必须要等待第一个线程执行完了以后再执行。所以整体上来讲我们看起来比较“慢”了,所以多线程的出现解决了这样的问题。所以我们点这里可以回答第一个问题,多线程解决的问题是充分的利用CPU 资源,使用多线程,避免CPU 出现空转,使线程调度器充分的使用CPU资源,让程序的效率更高。再打个比方使用0.05方天然气,可以煮一个鸡蛋,我一个一个能煮完,同时煮两个也能煮完(声明一下这里并不是说两个线程可以同时执行,一个时间 单位,CPU 只能执行一块),但是同样一起煮也能煮完,就是单位时间内干的活儿越多,那么效率就越高。 回答第2个问题,创建一个线程的方式我们知道的就有 Thread thread = new Thread(); new 一个线程 。2. 实现Runable 接口 重新run 方法 3.实现 Callable 接口 重新Callable 接口,至于Runable 的方式和Callable 接口的方式的区别我们后续再继续说道。 再来回答第3个问题,只是这个问题一般面试的时候想深入的问你线程之间状态的转换的时候才会从这个问题开始。 线程的5个状态分别是1. 新建(即为创建一个线程的几种方式) 2.就绪状态, thead.start() 当线程调用了start() 方法以后线程将进入待执行等待池,等待线程调度器来调用线程来执行线 3. 运行 (该状态是正在执行该线程中的run 方法(以runable接口Thread方法为主)) 4 阻塞 (该状态是正在执行的线程就卡在那儿了,但是不代表当前线程就不执行了)5.终结,当前线程已经执行结束了,也可以说线程的run 方法执行完了
先来说一下,创建一个线程时候JVM 内存分配区域划分,程序计数器,Java栈,本地方法是线程私有的,以及需要为线程分配一定的内存空间,所以创建一个线程是很消耗性能的。调用Thead.start() 干的就是我上面说描述的那些事情,只有当上面那些事情做完以后,线程才是进入到就绪状态。 我们重点说一下阻塞状态,如果当前只有一个线程,如果调用Thread.Sleep() 方法会使当前线程进入阻塞状态,这里Sleep 使用的是静态方法。源码的方法注释是说,使当前正在运行的线程进入睡眠状态,但是线程不会失去任何监听器的权利(锁) 也就是说当前线程会在执行的时候会进入睡眠,当睡眠时间过了以后又会继续执行下面的代码了。阻塞 2.wait() 这个方法特殊啊,因为每一个类中都有的方法,这个方法是所有的类都有这个方法。源码注释:当前线程等待直到其他线程调用当前对象的notify 或者notifyall 。换句话说次方法就是简单的执行呼叫,当前线程必须拥有该对象的监视器,线程释放监视器(锁)并且等待直到另外一个线程通知在此对象的锁上等待唤醒的线程。直到可以重新获得锁并且执行恢复执行。阻塞的方式还有join() 我们来看一下join() 方法中是怎么实现的,join()方法是同步阻塞。
使用join()关键字前:
上面画的线程状态切换图(如果有什么不对的地方希望各位大佬指正)。这里我们wait() 之后也是进入阻塞释放锁后,锁进入锁池,被其他相同锁竞争,当某一个线程调用notify 或者notifyAll的时候,这时候。我们的线程又可以进入锁池来竞争锁,以至于获取cpu 执行权了。join() 关键字注意一下join() 方法join(0) 等价于join() 在A线程中调用了B线程的join()方法,意思是A线程放弃了CPU 执行权将执行权交给B 线程,并且等待B线程执行完了以后并且销毁状态才到A 线程执行,我猜的是如果A 和B共处于一个锁,那么B线程在执行完程序进入到销毁状态的时候,调用了notifyAll 方法。还要注意join()方法一定要在start()方法之后调用。好了这里的内容看起来很浅,但是很多多线程的面试中,都是从这里开始的。