目录
一、对象的锁属性
Java每个类实例对象都有的 装自带属性的 对象头内存空间里面 有锁属性
1.作用
用来锁相对代码块 调度到 相对只单线程内地 此代码块执行
2.实现
2.1线程单代码块竞争锁
一个锁对象 初始为零持有层 只能拥单线程的个代码块竞争到持有,竞争到锁 每进一层代码块 就增一层持有层信息,执行完出一层代码块 就减一层持有层,竞争到锁的代码块 可以给子代码块 可重入加层地共用
2.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) {
}
}
}
二、线程卡住
遇阻象 找另路 地单路线搜索解决,当无另路 或另路循回阻象时 便卡住无法解决
死锁
当锁阻象的 另路循回阻塞时 便形成了死锁
解决方法
通过删除嵌套锁的代码结构 或统一用锁的顺序 就可破坏掉锁请求的环路结构,解决死锁
例:线程统一先用小锁再用大锁
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();
}
}
三、线程安全2
一线程读中 另线程进写时 遇到略读无感
1.不安全原因
【数据读取流向
(硬盘->)内存->寄存器->cpu
读硬盘到内存 相对 读内存到寄存器 非常慢、读内存到寄存器 相对 读寄存器到cpu 又非常慢】
编译器为提高代码执行效率,在保持单线程视野 逻辑不变的前提下 调整生成 优化执行的代码内容,但在多线程下 就可能会出现 视野缺陷下带来的出错优化:
在保障的单线程视野下,读取无变内存到寄存器时 可能会 简化只首读不变的一次 而略去 外线程修改内存的读取
- 主内存 是真正的定位置的内存
- 工作内存 是不定的 存储数据的"非内存"空间,通常包括 各种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();
}
}