如何停止正在运行的线程
1,使用退出标志,使线程正常退出(run方法中循环对退出标志进行判断)
2,使用stop()方法强行终止(不推荐)
3,调用interrupt()方法中断线程
打断阻塞线程(sleep,wait,join),线程会抛出InterruptedException异常
打断正常的线程,可以根据打断状态来标记是否退出线程
打断标记:线程.isInterrupted返回一个布尔值代表是否被打断
sychronized底层原理
Synchronized简介
同步锁,保证在同一时刻,被修饰的代码块或者方法只会被一个线程执行,从而保证线程安全.
Synchronized特性
锁对象:
java对象结构
可以分为三块布局:
示例数据(Instance Data)
存储了对象的实际数据
对象属性,父类属性,(数组长度)
对齐填充(Padding)
按8字节整数倍对齐填充
对象头(Header)
Mark Word + Class Metadata Address
M:
哈希码
分代年龄
(触发GC对象回收的次数,到达15就会从新生代转移到老年代)
GC标志
(GC记录对象是否存活)
锁信息
轻量级锁中的指针就是锁记录record
重量级锁中的指针就是指向关联的monitor
C:
类型指针(判断对象是哪个类的实例)
(数组长度)
锁类型
jdk1.6之前:
无锁
重量级锁:
底层通过java中的监视器锁(monitor)实现,每个java对象都有一个对应的监视器锁,只有获取了对象的监视器锁,线程才能执行同步代码块或者同步方法.
所有竞争锁失败的线程全部都会被阻塞挂起直到锁释放后被唤醒.
效率很低
monitor对象
jvm提供,c++实现
每个Java对象都可以关联一个monitor对象,一旦使用synchronized上锁后,这个对象的对象头的markword中就会设置指向这个monitor对象的指针
(notify,notifyall,wait)
获取对象锁就是获取monitor的所有权
当一个线程获取锁时,count++
owner设为这个线程
锁处于锁定状态
当通过wait()或执行完代码进行释放
就会复位相关状态->count–,owner设为空
获取锁的对象可以反复获取monitor对象,每获取一次count就++,
对应锁计数也需要多减一次,减到0锁才会释放
1.6之后:
偏向锁:
当第一个线程执行同步代码块时
不存在多线程竞争
锁就会偏向于这个线程,使用CAS将线程ID设置到自己的MD头,再次请求锁时,只需要判断markword中的锁标记是不是偏向锁,线程id是不是保持一致就可以直接请求到锁.
只会CAS一次
轻量级锁:
当第二个线程去申请锁时,锁就会升级为轻量级锁,在这种情况下,线程会进入CAS自旋而非挂起,不断的循环比较和替换(markword替换指向锁记录的指针),一直耗费cpu申请直到重新拿到锁,避免线程的阻塞和唤醒.
每次指向到 synchronized 代码块时,都会创建锁记录(Lock Record)对象,每个线程都会包括一个锁记录的结构,锁记录内部可以储存对象的 Mark Word 和对象引用 reference
用Object reference指向锁对象
用lock record(指向线程的地址)替换锁对象的对象头数据
解锁时交换回来(CAS)
jdk1.7后引入自适应自旋锁,根据锁自旋的结果来调整锁的自旋次数和是否阻塞.
自旋一定次数后,就会变为重量级锁
当锁发生重入,就会再添加一个锁记录来记录重入次数
对比
偏向锁:
只适用于一个线程情况
加锁解锁无需额外消耗,速度几乎相当于没加锁
轻量级锁:
只适用于少量线程竞争锁对象的情况且临界区较小,锁占用时间短
提高程序的响应速度,线程不会阻塞
但自旋会持续消耗cpu
重量级锁:
底层使用Monitor实现
适用于吞吐量大,锁占用时间长
不使用自旋消耗cpu
但线程挂起,响应时间缓慢
线程上下文切换->用户态和内核态的切换,成本较高
用户态:访问资源受限,权限较低
内核态:访问资源多,权限高
锁消除
在编译时进行扫描,去除不可能存在竞争的锁
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
//同步方法
public static String Test(String str){
StringBuffer sb = new StringBuffer();
sb.append(str);
sb.append(str);
return sb.toString
}
//因为sb对象仅在这个方法内生效,所以此时下面两个append是不存在线程安全问题的,方法上的锁将被自动消除.
锁粗化
通过扩大锁的范围,避免反复加锁和释放锁
public void Test() {
for (int i = 0; i < 100; i++) {
synchronized(lock){
........
}
}
}
public void Test() {
synchronized(lock){
for (int i = 0; i < 100; i++) {
........
}
}
}
HotSpot虚拟机
java虚拟机,将常用代码编译为原生代码执行,提高性能