多线程学习笔记
1.什么是线程
操作系统中 打开一个程序就是一个进程
一个进程可以创建多个线程
现在系统中 系统调度的最小单元是线程
2.多线程有什么用?
-
发挥多核CPU的优势
- 如果使用多线程 将计算逻辑分配到多个处理器核心上 会减少程序处理时间
- 防止阻塞
- 一个业务内部需要多个业务配合完成 如果是单线程 就会发生执行一件后再执行另一件,如果是多线程 可以多个业务逻辑 并发执行 快速响应用户请求 缩短响应时间
- 便于建模
- 比如有一个大任务 特别复杂 可以分解成多个小任务
分别建立程序模型,并通过多线程分别运行这几个任务 那就简单很多了。
3.什么是线程的优先级
在多线程运行的过程中,操作系统会分出一个个的时间片,当线程的时间片用完了就会发生线程调度,并等待下次分配。线程分配到多少时间片也就决定了线程使用处理器资源的多少,线程的优先级就是决定线程需要多少或者分配多少处理器资源的线程属性
通过priority控制 线程创建时 setPriority() 修改优先级 1-10 针对频繁阻塞的线程 设置较高的优先级 偏重计算的 设置较低的优先级 确保处理器不会被独占
4.线程有几种状态
1.new 初始化状态 线程被创建 但还没有调用start方法
2.runnable 运行状态 就绪和运行两种状态统称位运行中
3.blocked 阻塞状态 表示线程阻塞与锁
4.waiting 等待状态 表示线程进入了等待状态 当前线程需要等待其他线程做出一些特定的动作 通知或者中断
5.time_waiting 超时等待状态 指定时间自行返回
6.terminated 终止状态 表示 线程一件执行完毕
5.什么是Deamon线程
在程序中后台调度的线程 被用作完成一些支持性工作
如果java中已经没有了非deamon线程 则虚拟机就会退出
6.启动和终止线程
1.集成Thread类
2. 实现Runnable接口
3.Callable和Future创建线程
实现接口的方式比继承类的方式更灵活
启动 用start()方法
含义:当前线程同步告知java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。
线程中断:
调用线程的interrupt()方法进行中断
通过isIntterrupt()判断是否被中断
调用Thread.interrupted()对当前的中断标识进行复位。如果线程已经被终结 即使被中断过 在调用isIntterrupt()时也是返回false
7.过期的suspend() resume() stop()方法 暂停 恢复 停止
原因 suspend()调用后线程不会释放已经占有的资源(锁) 容易发生死锁。
被以后的等待/通知等机制来代替
8.安全的终止线程
通过设置标志位
通过调用interrupt()
private volatile boolea on = true;
public void run(){
while(on && !Thread.currentThread().isInterruped()){
i++;
}
}
public void cancel(){
on =false;
}
通过调用cancel()来终止线程
通过次方式终止时 线程会有时间去清理资源
9.线程间通信
每个线程都有自己的栈空间
如果每个线程孤立运行 没有什么价值
1.对变量 加volatile关键字
告知程序 任何程序对该变量的访问都要从共享内存中去获取,对它的任何修改都要同步刷新回共享内存中。保证了它对所有线程的可见性。
如果过多的使用volatile 回降低程序执行的效率。
2.通过关键字 synchronized 修饰方法或者同步快
确保多个线程在同一时刻 只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
10.等待和通知
wait() 调用该方法的线程进入waiting状态 只有等待另外线程的通知或被中断才会返回,调用次方法会释放对象锁。
wait(long) 超时等待一段时间,参数 毫秒 如果没有通知就超时返回。
wait(long int)对超时时间更加细粒度控制 可以达到纳秒。
notify() 通知一个在对象上等待的线程,使其从wait()方法返回,返回的前提是该线程获取了对象的锁。
notifyAll() 通知所有等待在该对象上的线程。
经典范式
等待方:
- 获取对象锁
- 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件
- 条件满足 则执行对应逻辑
synchronized(对象){
while(条件不足){
对象.wait();
}
对应的处理逻辑
}
通知方:
1.获得对象的锁。
2.改变条件
3.通知所有等待在对象上的线程
synchronized(对象){
改变条件
对象.notifyAll();
}
11. Thread.join() 当前线程等待thread线程终结之后才从thread.join()返回。
与上面的等待通知机制一个原理
12.ThreadLocal的使用
线程变量 以ThreadLocal对象为键 任意对象为值 存储的存储结构
一个线程可以根据一个ThreadLocal对象擦好像到绑定在这个线程的值
13.等待超时模式
等待时间
synchronized(对象){
将来时间=当前时间+等待时间
while(条件不足&&等待时间>0){
对象.wait(等待时间);
等待时间=将来时间-当前时间
}
对应的处理逻辑
14.线程安全的概念
当多个线程访问某个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)是安全的。
锁
15.lock接口
锁是用来控制多个线程访问共享资源的方式 ,一般来说 一个锁能防止多个线程同事访问共享资源。排它锁。
lock具备synchronized关键字不具备的主要特征:
尝试非阻塞地获取锁
能被中断地获取锁 能够响应中断 当获取到锁的线程被中断时 中断已超将被抛出 并释放锁
超时获取锁 在指定的时间之前获取锁 如果时间到了 无法获取则返回
lock基本操作
lock() 获取锁
lockInterruptibly()可中断获取锁 就是获取锁后 可以响应中断。
tryLock() 尝试非阻塞的获取锁 获取到则返回true 否则返回false
tryLock(time,unit)超时获取锁 1在设置时间内获得锁 2 在设置时间内被中断 3 超时 结束 返回false
unlock() 释放锁
newCondition() 获取等待通知组件 该组件和当前的锁绑定 当前线程只有获得锁 才能调用该组件的wait()方法 而调用后 当前线程将锁释放掉。
多个线程多个锁:多个线程每个线程都可以拿到自己指定的锁 获得锁后 再执行synchronized方法体的内容 在静态方法上加锁 等于在这个类上加锁
原子性 一致性 隔离性 永久性