Java线程学习(一)
线程生命周期
stateDiagram-v2
direction LR
s1 : 就绪状态
s2 : 运行状态
s3 : 阻塞状态
s4 : 锁池(lockpool)
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
[*] --> s1::: badBadEvent : 调用start方法
s1 --> s2::: badBadEvent : JVM的调度
s2 --> s1 : JVM的调度(yield方法)
s2 --> s3::: badBadEvent : 遇到阻塞事件(sleep/join方法)
s2 --> s4 : synchronized
s4 --> s1 :
s3 --> s1 : 阻塞解除
s2 --> [*] : run方法结束
初始是新建状态:刚新建出来的线程对象
就绪状态:就绪状态的线程又叫做可运行状态,表示当前线程具有抢夺CPU时间片的权利(CPU时间片就是CPU执行权),当一个线程抢夺到CPU时间片之后,就开始执行run方法,run方法的开始执行标志着线程进入运行状态
运行状态:run方法的开始执行标志着这个线程进入运行状态,当之前占着的CPU时间片用完之后,会重新回到就绪状态继续抢夺CPU时间片,当再次抢到CPU时间片之后,会重新进入run方法接着上一次的代码继续往下执行
阻塞状态:当一个线程遇到阻塞事件,例如接收用户键盘输入,或者sleep方法等,此时线程会进入阻塞状态,阻塞状态的线程会放弃之前占有的CPU时间片。阻塞状态解除时,之前的CPU时间片没了,需要再次回到就绪状态,抢夺CPU时间片
锁池(lockpool):锁池不是一种状态,便于理解的话可以当成一种阻塞状态。线程进入锁池找共享对象的对象锁的时候,会释放之前占有的CPU时间片,有可能找到了,也有可能没找到,没找到则在锁池中等待,如果找到了会进入就绪状态继续抢夺CPU时间片
结束是死亡状态:run方法结束
synchronized三种写法:
第一种:同步代码块
1
2
3synchronized (线程共享对象) {
// 线程同步代码块
}第二种:在实例方法上使用synchronized
表示共享的对象是调用该实例方法(不包括静态方法)的对象
并且同步代码块是整个方法体第三种:在静态方法上使用synchronized
表示找类锁
因为静态方法是不属于当前实例的,而是属于类的,因此锁的是类
类锁永远只有一把
就算创建了100个对象,那类锁也只有一把
开发中如何解决线程安全?
- 尽量使用局部变量代替实例变量和静态变量
- 如果必须使用实例变量,可以考虑创建多个对象,这样实例变量的内存就不共享了
- 如果不能使用局部变量,也不能创建多个对象,此时考虑使用synchronized