并发的发展历史
- 真空管和穿孔打卡
- 作用:只是解决简单的数学问题
- 运行方式:
- 先把程序写到纸上,打孔穿成纸片,把卡片送到指定的输入室
- 在输入室将程序输入到计算机上
- 计算机运行当前的任务,将结果输出到打印机上
- 缺点:只能运行一个程序,资源利用率低
- 晶体管和批处理系统
- 作用:解决了解决计算机的空闲问题
- 运行方式:
- 程序员把卡片拿到1401机(读取卡片的机器)
- 1401机(读取卡片的机器)把批处理作业读到磁带上
- 操作员把输入磁带送到7094机(运行程序的机器)
- 7094 机(运行程序的机器)进行计算
- 操作员把输出磁带送到 1401机(输出结果的机器)
- 1401机(输出结果的机器)打印输出
- 缺点:虽然解决的计算机闲置的缺点,但是如果其中一个步骤的上一步还没有做完,就会形成堵塞而暂停,这就属于资源的严重浪费
- 集成电路和多道程序设计
- 作用及运行方式:解决了资源严重浪费,就是把内存分为几个部分,每一个部分放不同的程序。当一个程序需要等待I/O 操作完成时。那么 CPU 可以切换执行内存中的另外一个程序。
- 进程:进程的本质是一个进程的本质是一个正在执行的程序,程序运行时系统会创建一个进程,并且给每个进程分配独立的内存地址空间保证每个进程地址不会相互干扰。同时,在CPU对进程做时间片的切换时,保对进程做时间片的切换时,保证进程切换过程中仍然要从进程切换之前运行的位置出开始执行。
- 结论:有了进程以后,可以让操作系统从宏观层面实现多应用并发。而并发的实现是通过 CPU 时间片不端切换执行的。对于单核 CPU 来说,在任意一个时刻只会有一个进程在被CPU 调度
- 线程的出现
- 为什么有了进程的情况下还会有线程的产生?
- 答:
- 因为CPU由单核变成了多核,利用多线程可以成功实现并行操作
- 在一个应用进程中也会存在多个同时执行的任务,如果其中一个任务被阻塞,将会引起不依赖该任务的任务也被阻塞 。 通过对不同任务创建不同的线程去处理,可以提升程序处理的实时性
- 线程可以认为是轻量级的进程,所以线程的创建、销毁比进程更快
- 答:
- 为什么有了进程的情况下还会有线程的产生?
Java中线程的应用
- 实现Runnable的接口
- 继承Thread类(本质上也是实现Runnable接口)
- 实现Callable 接口通过 FutureTask 包装器来创建 Thread 线程
- 作用场景:当我们需要线程完成时返回一个值给主线程,而主线程需要以来这个值来进行下一步操作的时候,就需要用到这个方法
- 通过线程池来实现(ThreadPool)
并发基础
生命周期
- 线程的生命周期分为六个状态:
- NEW:初始状态,线程被构建,但是还没有调用 start 方法
- RUNNABLED:运行状态 JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中”
- BLOCKED:阻塞状态,表示线程进入等待状态 也就是线程因为某种原因放弃了 CPU 使用权,阻塞也分为几种情况
- 等待阻塞:运行的线程执行 wait 方法, jvm 会把当前线程放入到等待队列
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么 jvm 会把当前的线程放入到锁池中
- 其他阻塞:运行的线程执行 Thread .sleep 或者 t .join 方法,或者发出了 I /O 请求时, JVM 会把当前线程设置为阻塞状态,当 sleep 结束、 join 线程终止、 io 处理完毕则线程恢复
- TIME_WAITING:超时等待状态,超时以后自动返回
- TERMINATED:终止状态,表示当前线程执行完毕
线程的启动
- 线程的启动可以使用 start() 方法 run 方法中的代码执行完毕以后,线程的生命周期也将终止。调用 start 方法的语义是当前线程告诉 JVM ,启动调用 start 方法的线程。
线程的终止
- 如何终止线程
- 使用 stop 方法,但是stop方法并不能导致线程资源的正常释放,因此会导致程序可能出现一些不确定的状态
- 使用 interrupt 方法可以中断线程
- 当其他线程通过调用当前线程的 interrupt 方法,表示向当前线程打个招呼,告诉他可以中断线程的执行了 ,至于什么时候中断,取决于当前线程自己。
- 线程通过检查资深是否被中断来进行相应,可以通过 isInterrupted() 来判断是否被中断。
- 总而言之,通过 interrupt ,设置了一个标识告诉线程可以终止了。