线程:
- 线程是由进程创建的,是进程的一个实体(迅雷这个进程中下载多个电影【多个线程】)
- 一个进程可以拥有多个线程
线程相关概念:
- 单线程:同一时刻,只允许执行一个线程
- 多线程:同一时刻,可以有多个线程
- 并发:同一时刻,多个任务交替执行,造成一种貌似同时的错觉,简单的说,单核cpu实现的多任务就是并发
- 并行:同一时刻,多个任务同时执行,多核cpu可以实现并行(2个cpu4核8线程),所谓串行,就是执行完一个才去执行另一个线程
- 也有可能并发和并行同时进行
线程的基本使用:
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
- 当一个类继承了Thread类,该类就可以当成线程使用
- Tread类实现了Runable里的run方法
- Jconsole监控线程
Start源码:最核心的就是start0
Start0是本地方法,是JVM调用,底层是c/c++实现,真正实现多线程的效果,而不是run方法,run方法什么时候执行,由cpu统一调度
实现Runnable接口
- 由于java是单继承的,在某些情况下一个类可能已经继承了某个父类,这是在用继承Thread类方法来创业简称显然不可能
- Java设计者还提供了另一个方式创建线程,就是通过Runnable接口来创建线程(这里底层使用l设计模式,代理模式)线程代理类就是模拟了一个极简的Thread
实现Runnable接口方法更适合多个线程共享一个资源的情况,并且避免了单继承的限制(天书奇谈双开!!!,一个修改器开俩号)
为什么sleep要抛出中断异常
假设sleep()方法不抛出中断异常,也就是线程没有中断响应能力,会怎么样?
考虑如下场景:
线程A:sleep中
线程B:A别睡了,要关机啦(向A发送中断信号)
线程A:sleep中
这样好吗?这不好。因为线程A对外界情况没有感知能力。
中断就起到了这样的作用:让线程具有感知的能力。sleep(),wait()等方法都需要你去处理中断异常,也就是需要你的代码能够响应中断。
线程终止:
- 当线程完成后,会自动退出
- 还可以通过使用变量来控制run方法退出的方法是停止线程,即通知方式
线程常用的方法:
注意事项:1.start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程。
2线程优先级的范围: MIN_PRIORITY = 1; NOR_PRIORITY = 5; MAX_PRIORITY = 10;
- Interrupt,终端线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
- Sleep线程的静态方法。使当前线程休眠
- Yield:让出cpu,让其他线程执行,但礼让的时间不确定,所以不一定礼让成功。
- Join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插队的线程所有的任务
用户线程和守护线程:
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
守护线程:
必须先设置守护线程再开始运行守护线程
线程的声明周期:
线程状态:NEW 尚未启动的线程出于此状态
RUNNABLE 在Java虚拟机中可运行状态,细化分程两种 Ready Running
Teminated终止状态
在RUNNABLE状态等待进入同步代码块的锁,会进入Blocked
在RUNNABLE状态调用wait,join , LockSupport.park,会进入Waiting
在RUNNABLE状态sleep, wait , join, LockSupport.parkNanos, LockSupport.parkUnit会进TimedWaiting
线程同步机制:Synchronized
- 在多线程变成,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
具体同步方法:
- 同步代码块 synchronized(对象){得到对象的锁,才能操作同步代码}
- Synchronized还可以放在声明中,表示整个方法-为同步方法
Public synchronized vpoid m(String name){需要被同步的代码}
互斥锁:
- java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象队形一个可称之为”互斥锁“的标记,这个标记用来保证在任一时刻,指能有一个线程访问该对象
- 关键字synchroni则带来与对象的互斥锁练习,当某对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一对象)
- 同步方法(静态的)的锁是当前类的
注意事项:
1.同步对象没有使用static修饰:默认锁对象为this(保证是大家共享的一个对象
)例如
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤:
需要先分析上锁的代码
选择同步代码块或同步方法
要求多个线程的锁对象为同一即可
这样this才能锁住后面的,如果是多个不同对象的线程,每个人都有自己的一套锁(对应其他的门),随便进,那这个互斥锁还有什么意义,必须保证只有一把锁(就是保证只有一个门),大家才能乖乖排队
线程的死锁
基本介绍:多个线程都占用了对方的锁资源,但不肯让,导致了死锁,在编程是一定要避免死锁的发生
下面操作会释放(开)锁:
- 当前线程的同步方法、同步代码块执行结束 案例:上厕所,完事后,锁会自动开(释放锁就是开锁)
- 当前线程在同步代码块、同步方法中遇到break、return
- 当前线程在同步代码块、同步方法中出现了未处理的Error或Exceptiomn,导致异常结束
- 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去,开锁让其他小伙伴先进
下面的操作不会释放锁:
- 线程执行同步代码块或同步方法时,程序调用了Thread.sleep、Thread.yield方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上眯了一会
- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
提示赢尽量避免使用suspend()和resume()来控制线程,方法不在推荐使用