一,线程与进程
先来解释一下线程和进程的东西
1,线程:进程中负责程序执行的执行单元
线程本身依靠程序进行运行
线程是程序中的顺序控制流,只能使用分配给程序的资源和环境
2,进程:执行中的程序,一个进程至少包含一个线程。
3,单线程:程序中只存在一个线程,实际上主方法就是一个主线程
4,多线程:在一个程序中运行多个任务,目的是更好地使用CPU的资源
二,线程的实现:
线程的实现有两种,一种是继承Thread类 一种是实现Runable接口
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法 是 一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动 新线程并执行自己定义的run()方法。
三,线程的状态:
创建(new)状态:准备好了一个多线程的对象
就绪(runnable)状态:调用了start()方法,等待CPU进行调度
运行(running)状态:执行run()方法。
阻塞(blocked)状态:暂停停止执行,可能将线程交给其他线程使用
终止(dead)状态:线程销毁
当需要新起一个线程来执行某个子任务时,就创建了一个线程,但是线程创建之后,不会立即进入就绪状态。
因为线程需要创建一些条件,只有线程运行需要的所有条件都满足了,才进入就绪状态。
当线程进入了就绪状态后,不代表立即就能获取CPU执行时间,也许此时CPU正在执行其他事情,
因此需要等待,当CPU执行时间之后,线程便会真正进入了运行状态
线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠
睡眠一定时间后再执行,用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting
(睡眠或等待一定的时间),waiting(等待被唤醒),blocked(阻塞);
当由于突然中断或者子任务执行完毕,线程就会自动消亡
在有些教程上将blocked,waiting,time waiting统称为阻塞状态,这个也是可以的,只不过这里我想将
线程的状态和JAVA中的方法调用联系起来,所以将waiting和 time waiting两个状态分离出来。
sleep(睡眠)和wait(等待被唤醒)的区别:
sleep是Thraed类的方法,wait是Object类中定义的方法。
Thread.sleep不会导致锁行为的改变,如果当前线程是拥有锁的,那么Threed.sleep不会让线程释放锁
Thread.sleep和Object.wait都会暂停当前的线程。OS会将执行时间分配给其他线程,区别是,调用wait后
需要别的线程执行motify/notifyAll才能够重新获得CPU执行时间
上下文切换
对于单核CPU来说,CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个程序,这个叫做线程上下文切换
由于可能当前线程的任务并没有执行完毕,所以在切换是需要保存线程的运行状态,以便下次重新切换回来时
能够来回切换之前的状态运行。举个简单的例子:比如一个线程A正在读一个文件内的内容,正读到文件的有Ibanez,此时需要暂停A
去执行B,当再次切换线程A的时候,我们不希望线程A又从文件的开头来读取
因此需要记录线程A的运行状态,那么会记录那些数据呢?
因为下次恢复时要知道在这之前当前线程已经执行到那条指令了。所以需要记录程序计数器的值,另外比如说线程正在进行
没结束计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起的变量的值是多少,因此需要记录CPU寄存器的状态。
所以一般来说,线程上下文切换过程中会记录表程序计数器,CPU寄存器状态等数据
简单来说:对于线程的上下文切换实际上就是 存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。
虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以
在进行编程时要注意哪些因素
线程的常用方法
public void start() 使线程开始执行,
JAVA虚拟机调用该线程的run()方法