【JavaEE】锁(一):对象的锁属性[作用/实现/修饰/危害]、线程死锁、线程安全(二).1:不安全原因和措施



目录
一、锁(一):对象的锁属性[作用/实现/修饰/危害]
知识连接:
锁(四):synchronized&ReentrantLock

Java每个类实例对象都有的 装自带属性的 对象头内存空间里面 有锁属性
1.作用
用来锁相对代码块 调度到 相对只单线程内地 此代码块执行
2.实现
2.1线程单代码块竞争锁
一个锁对象 初始为零持有层 只能拥单线程的个代码块竞争到持有,竞争到锁 每进一层代码块 就增一层持有层信息,执行完出一层代码块 就减一层持有层,竞争到锁的代码块 可以给子代码块 可重入加层地共用
2.2锁对象判持有层信息
锁对象 用线程代码块持有层信息为
- 竞争到的非零持有层线程代码块 供锁保障
- 零持有层线程代码块 阻塞运行、供竞争机会,无锁就会被阻塞,处于可参与竞争的现阻塞状态
- 锁的使作
1. 已被一个线程占有的锁 使等 正向获取锁的其它线程
2. 未被占有的锁 被 正向获取锁的线程竞争
3.修饰
(1)synchronized修饰静态方法,是以 类对象 为锁对象 加锁方法的整个代码块
(2)synchronized修饰非静态方法,是以 this本实例对象 为锁对象 加锁方法的整个代码块
class Counter {
public int count;
//increase1、2等效:
synchronized public void increase1() {
count++;
}
public void increase2() {
synchronized (this) {
count++;
}
}
//increase3、4等效:
synchronized public static void increase3() {
}
public static void increase4() {
synchronized (Counter.class) {
}
}
}
- synchronized锁特点
synchronized 是 可重入、自适应、非公平、互斥 的锁
4.危害
有加锁就会带来的 锁竞争、锁阻塞 会使cpu性能大幅下降
锁的开销
1. 加锁 有 获取锁 的开销
2. 解锁 有 释放锁 的开销
二、线程卡住

遇阻象 找另路 地单路线搜索解决,当无另路 或另路循回阻象时 便卡住无法解决
死锁
当锁阻象的 另路循回阻塞时 便形成了死锁
解决方法
通过删除嵌套锁的代码结构 或统一用锁的顺序 就可破坏掉锁请求的环路结构,解决死锁
例:线程统一先用小锁再用大锁
class Test {
private static Object locker1 = new Object();
private static Object locker2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (locker1) {
// sleep 原先确保 t1和t2都分别拿到loker1和loker2后 再进行后续动作 发现死锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (locker2) {
System.out.println("加锁成功!");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (/*locker2->*/locker1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (/*locker1->*/locker2) {
System.out.println("加锁成功!");
}
}
});
t1.start();
t2.start();
}
}
三、线程安全(二).1:不安全原因和措施
一线程连读 另线程进写 遇到已选择略读的无感
知识连接:
1.不安全原因
【数据读取流向
(硬盘->)内存->寄存器->cpu
读硬盘到内存 相对 读内存到寄存器 非常慢、读内存到寄存器 相对 读寄存器到cpu 又非常慢】
编译器为提高代码执行效率,在保持单线程视野 逻辑不变的前提下 调整生成 优化执行的代码内容,但在多线程下 就可能会出现 视野缺陷下带来的出错优化:
在保障的单线程视野下,要多次读取 单视野固定无变内存 到寄存器时 可能会 简化成 只首读不变的一次 而错过 外线程修改内存时的 如果那时还在读 的正常读取
- 略掉读取是线2犯的错,如果就算没略掉 读取也未同步,线1也犯错
- 主内存 是真正的定位置的内存
- 工作内存 是不定的 存储数据的"非内存"空间,通常包括 各种cpu寄存器和缓存
2.措施
用volatile确定 不会对内存略读,保障了内存的可见性
class Test {
private static volatile int isQuit = 0;// volatile 确定不会对isQuit进行内存略读
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (isQuit == 0) {
// 循环体里啥都没干,此时意味着这个循环,一秒钟就会执行很多很多次
// 编译器在单线程视野内 对内存无变的isQuit 可能会 只首读不变的一次,以略读来提升效率
}
System.out.println("t1 退出!");
});
t1.start();
Thread t2 = new Thread(() -> {
System.out.println("请输入 isQuit: ");
Scanner scanner = new Scanner(System.in);
// 一旦用户输入的值不为0 就会使t1线程执行结束
isQuit = scanner.nextInt();
});
t2.start();
}
}
![]()
更多推荐




所有评论(0)