什么是线程:
操作系统运算调度的最小单元,轻量级的进程,一个线程包含独立的计数器、栈、局部变量等属性,并且线程间可以共享堆内存。所谓的多线程程序,也就是处理器通过上下文切换,给不同线程分配时间片,让人感觉线程是同时执行的。
为什么要使用多线程:
充分利用多核处理器
一个进程下会有多个线程,一个线程的运行会占用一个处理器核心。现在多核cpu已经司空见惯,如果我们编程还是单线程,那么多核cpu中只会有一个核心被使用,其他核心被闲置。为了重复利用cpu多核资源,提高运算能力,我们使用多线程,同一时间可以在cpu的多核上运行多个线程。更快的响应体验
比如我们请求一个页面,如果是单线程,会挨个获取这个页面上的视频、图片、文字等等资源;如果是多线程,会并发回去这个页面上的资源,以缩短页面加载的时间。更快的速度,更好的用户体验。
线程的优先级:
线程分配到的时间片多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。
线程的状态:
NEW: 线程构建完毕,但是还没有开始运行(还未执行thread.start());
RUNNABLE: 线程的就绪与线程的运行都称为RUNNABLE状态;
BLOCKED: 阻塞状态。线程等待获取其他线程通过Synchronized持有的锁时,呈现BLOCKED状态
WAIT: 等待状态;Lock锁阻塞;object.wait();Thread.join();LockSupport.part()呈现的状态
TIME_WAITING: 有超时限制的方法通常执行后呈现TIME_WAITING状态.相当于在WAIT状态基础上增加了超市限制。如,Thread.Sleep(long n);;Thread.join(long n)
TERMINATE: 线程结束的状态
Daemon线程:
Daemon线程是一种支持型线程,主要被用作程序中后台调度以及支持性工作。当一个java虚拟机中不存在非daemon线程时,java虚拟机将会退出。
注意:无法保证Demon线程中的finally块一定执行。
线程的构造:
线程的构建也就是初始过程,通常一个线程由其父进程创建:父进程为子进程分配空间,子进程继承InheritableThreadLocal、继承了classLoader、继承是否Demon,并且子进程获取到了一个唯一ID
线程的中断:
线程的一个标识位属性,表示一个运行中的线程是否被其他线程进行了中断。
isIntrrupted()方法:判断是否被中断;
Thread.interrupted():对当前线程的中断标识位复位;
- 如果该线程已经处于终结状态,即时该线程被中断过,该线程对象的isInterrupted()方法返回false;
- 申明抛出InterruptedException的方法,在抛出异常之前,java虚拟机先清除中断标识位,调用isInterrupted方法返回false;
安全终止线程:
- 通过中断标志;
- 通过boolean变量;
线程间的通信:
- volatile:用来修饰字段(成员变量),告知程序任何对该变量的访问都需要从共享内存中获取,对它的改变必须同步刷新回共享内存,保证所有线程对变量访问的可见性;
- synchronize:修饰方法或者同步快;确保多个线程在同一时刻,只能有一个线程处于方法或者同步快中,保证了线程对变量访问的可见性和排他性;
- 等待/通知机制:一个线程修改了一个对象的值,而另一个线程感知到了变化,进行操作,前者为生产者,后者为消费者。
存在的问题:
- 难以确保及时性:在睡眠中,基本不消耗处理器资源,但是如果睡得过久,就不能及时发现条件已经变化;
- 难以降低开销:降低睡眠的时间,可能会消耗更多的处理器资源;
解决方法:等待/通知机制;
- notify():通知一个在对象上等待的线程,使其从wait()方法返回,而返回的条件是该线程获取到了对象的锁;
- notifyAll():通知所有等待在该对象上的锁;
- wait():调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,调用wait()方法后,会释放对象的锁;
- wait(long):超时等待一段时间,参数为毫秒,没有通知就超时返回;
- wait(long,int):超时时间更细粒度的控制,可以达到纳秒;
等待(消费者)/通知(生产者)的经典范式:
等待方原则:
- 获取对象的锁;
- 如果条件不满足,那么调用对象的wait方法,被通知后依然要检查条件;
- 条件满足则执行相应的逻辑;
伪代码:
synchronized(对象)
{
while(条件不满足)
{
对象.wait();
}
对应的处理逻辑;
}
通知方原则:
- 获取对象的锁;
- 改变条件;
- 通知所有等待在对象上的线程;
伪代码:
synchronized(对象){
改变条件
对象.notifyAll();
}