1)只有锁抢占时,才BlockED,如:synchronized方法,t1获得了锁,t2返回的状态则是Blocked
2)waiting:
wait
join
LockSupport.park
3)jps: 显示所有的Java进程
4)jstack 848700(线程id)
5)start:
start-->start这个是native方法
------------------------多线程原理分析------------------------
1)线程安全问题:
比如:i++:
单线程下没有问题。
多线程下:2个线程同时访问,是先get i,再++,如果同时拿到了0,则 <=2;
2)线程安全性:
管理对数据状态的访问:
数据一定是共享的。
数据在生命周期里,一定会变化。 像final,除了构造函数,则没机会修改。
3)一个线程去更新的时候,拿到的值在于并不是上一次线程更新后的值。
4)分布式锁
5)锁--》互斥: synchronized
1.6偏向锁,轻量级锁的概念,对锁进行了性能优化。 既要保证锁的安全性,又要保证性能。
1.修改实例方法
只锁定当前实例,对象锁
public synchronized void test(){
}
和上面的一样,但是锁的力度不一样
public void test(){
synchronized(this){
}
}
类级别的
public synchronized static void test(){
}
类级别的
public static void test(){
synchronized(Demo.class){
}
}
2.修饰静态方法
3.修饰代码块
对象的生命周期决定的
数据库:
行锁
表锁
对象锁、类锁: 去呗字啊与是否跨对象跨线程被保护
类锁:只有一个。 伴随着进程的生命周期结束和启动的。
setNx(key, value): 0/1的互斥结果。多个进程设置时,只能设置成功一个。数据的存储和互斥
对象在jvm中存储,会有一个内存布局:
对象头:
age
锁标记
偏向锁标记
。。。
实例数据
填充数据
6)synchronized
不同锁的类型
什么时候触发各种锁
无锁状态:对象的HashCode + 对象分代年龄+ 状态位001
GC标记
轻量级锁: 只想栈中锁记录的指针。
锁是可以升级的
共享:多个对象都能访问到
互斥:互斥特性,只有一个线程能访问
对象头: MarkWord
instanceOop
无锁--》CAS偏向锁--》轻量级锁--》重量级锁:
加锁保证安全性的同时,还要保证性能。 因此会有这几种锁。是锁住小区大门,还是自己家的门。
1.6之前是重量级锁。是直接把线程挂起来。涉及到os内核进行线程上下文切换。
还有一种,无锁化:
有3种可能:
只有线程A去访问(大部分情况是属于这种):
HotStop作者发现大部分是这种,因此引入了偏向锁。
A的ThreadID,偏向锁标记1,
我们最后一定会关闭偏向锁。
A和B交替访问。
A拿到后,此时是偏向锁。
B接着去尝试获得锁,获取失败。 这时,就升级为轻量级锁(自旋)。
B如果还获取不成功,那么只能让B线程阻塞。而1.6之前,则直接就是挂起。
多个线程同时访问。
最后只能升级到:阻塞状态。动口解决不了,只好动手。
7)偏向锁
CAS比较实现原子性
不能直接通过if比较
乐观锁
CompareAndSwap(value, expect, update):
DB中通过version字段,
MarkWord(32位):
ThreadID(23) epoch(2) age(4) 1(1) 01(2)
撤销偏向锁,升级为轻量级锁,从而接下来2个线程竞争将以轻量级锁的方式去竞争
前提:
大部门都是只有一个线程去访问。
8)1.6之前是重量级锁,为了优化性能,最开始最好肯定是不加锁。
hotspot作者发现了实际情况,从最基本的锁开始,就想到用升级的过程去提升性能,
最开始就是:没有锁,同时又实现数据安全性,然后就出现了偏向锁的概念,偏向锁是一般都是一个锁去访问到共享数据,
然后继续真的有线程竞争了,就不断出现锁的升级去解决问题: 偏向锁--》轻量级锁--》重量级锁
9)轻量级锁 CAS: 自旋
通过无意义的循环,去判断
boolean cas(){
if(flag){
....
return; // 获取锁成功
}
}
前提: 大部分的线程在获取锁之后,在非常短的时间会去释放锁。 而:如果直接让线程阻塞,那开销就大。
自旋会浪费CPU资源,如果经历了一定的时间或者次数,还没有获得锁,那么轻量级锁就会升级为:重量级锁。也就是阻塞起来,就安静了。
自旋次数:
设置自旋次数 preBlockSpin
自适应自旋
MarkWord:
LockRecord
10)重量级锁:
升级到重量级锁后,没有获得锁的线程,将会变为BLOCKED状态,重量级锁是基于监视器来实现的。 ObjectMonitor。 所以任何对象都可以成为锁。
monitor是基于MutexLock(互斥锁)--》如果monitorrenter成功,则说明获得了锁,系统级别的线程切换(内核态 用户态)
同步队列(每一个被阻塞的线程,会被加入到这个队列中),monitorexit的时候,就会唤醒队列中的线程。
11)wait、notify、notifyall
sychronized(lock){
lock.notify(); // 如果没有获得锁,无法唤醒别人
}
不加sychronized会有2个问题:
会抛出异常错误。
为了感知要释放哪个锁。
wait的2个作用: 实现线程的阻塞 + 释放当前的同步锁
毕竟自己wait不释放,那么其它线程无法获取到锁,也无法通知它被唤醒。
notify/notifyall: 唤醒1个/唤醒所有
12)偏向锁,就是重入锁
13)轻量级锁:AQS有实现
14)重量级锁:AQS也有实现
15)AQS是一种同步队列
16)wait:释放锁资源和cpu资源。
sleep: 只是变为waiting状态。同时释放CPU资源。
17)场景--》需求--》解决方案--》应用--》原理
场景:多线程场景
需求:多线程并行产生的线程安全性
解决方案: 加锁(synchronized)
应用: synchronized的集中用法。 实例锁、静态方法锁、代码块
原理: 偏向锁(前提是:大部分情况下只有一个线程去访问锁对象)--》轻量级锁(CAS 自选锁)--》重量级锁(mutex 互斥锁)
18)volatile: 可见性、原子性、有序性