线程学习笔记

线程安全性

  • 原子性:synchornized、AtomicXXX、Lock

​ 原子性是指汇编指令不可拆分的,如同数据库中的事务,要么全部成功,要么全部失败一样

  • 可见性:synchornized、volatile
  • 有序性:synchornized、volatile

java中的同步锁synchronized

synchronized锁范围有实例级别和class级别.

public static int i = 0;
public void increment() {
    // 
    synchronized (this) {
        i++;
    }
    // 修饰class级别的,静态方法也是JVM中唯一的,所以这个和静态方法一样
    synchronized (Demo1.class) {
        i++;
    }
}

public static void main(String[] args) {
    Demo1 demo1 = new Demo1();
    Demo1 demo2 = new Demo1();
    // 修饰实例类级别的锁
    synchronized (demo1) {
        System.out.println("这个是demo1持有的锁");
    }
    synchronized (demo2) {
        System.out.println("这个是demo2持有的锁");
    }
}

作用范围

  • 修饰实例方法
  • 静态方法
  • 修饰代码块

对象和锁标记存储的关系

锁的标记或者锁的竞争依赖于对象。影响锁的作用范围,本质上就是对象的生命周期。

而锁标记会存储在对象头中。

  • InstanceOOP

An InstanceOOP is an instance of a java class.

这里需要对JVM有一定的了解,顺便附上JVM的官网链接

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html

What is java stack? 
Each Java Virtual Machine thread has a private Java Virtual Machine stack, created at the same time as the thread. A Java Virtual Machine stack stores frames (§2.6). A Java Virtual Machine stack is analogous to the stack of a conventional language such as C: it holds local variables and partial results, and plays a part in method invocation and return. Because the Java Virtual Machine stack is never manipulated directly except to push and pop frames, frames may be heap allocated. The memory for a Java Virtual Machine stack does not need to be contiguous.

在这里插入图片描述

Demo1.class,经过类加载后,进入到JVM的元空间的方法区,存储类信息,会产生一个java.lang.Class实例,而这个实例则存在堆里边。

// 获取Hello类的InstanceKlazz
Clazz clazz = Hello.class;
// 获取到Hello类的实例,在堆内存的实例中,会有一个元数据指针,指向了方法区里边的Hello.class
Hello h = new Hello();

markOOP

在这里插入图片描述

锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞

争的激烈而逐渐升级。

在这里插入图片描述

在jdk1.6之前,synchronized是重量级锁。带有线程的阻塞和唤醒的锁,我们称为重量级锁。这个动作会涉及到内核态的切换,内核态的切换会涉及到CPU的指令,就会有时间片切换的问题,带来一定的性能开销;为了去减少这种开销,synchronized在这里做了优化,在能不阻塞的情况下,去竞争到锁资源。

偏向锁

数据安全性和性能的平衡

轻量级锁

属于一种自旋锁,带有自适应重试机制,会带来CPU资源的浪费,但相对于重量级锁,会有一定的性能平衡。

重量级锁

带有线程的阻塞和唤醒的锁,我们称为重量级锁

锁的升级

在这里插入图片描述

CPU上下文切换

CPU时间片切换会带来一些性能损失,主要是因为

  • CPU寄存器和执行位置的保存
  • 多CPU之间的缓存数据

CAS

compareAndSwap

ABA问题:增加version去判断

线程可见性、有序性

硬件、操作系统(线程)、编译器(优化)会导致线程安全问题.

程序寄存器、L1 Cache/L2 Cache/L3 Cache(CPU多级缓存)、

而增加CPU高速缓存这个优化也会带来缓存一致性问题。缓存不一致就会带来可见性问题。

解决缓存一致性问题的方案有 MESI协议

volatile关键字

可以避免编译器优化,以及解决CPU缓存不一致的问题,从而达到可见性。

另外volatile也可以解决有序性的问题。

线程可见性规则之Happens-Before原则的定义

  • 程序次序规则(Program Order Rule):在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。

  • 管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。

  • volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作。

  • 线程启动规则(Thread Start Rule):Thread对象start()方法先行发生于此线程的每一个动作。

  • 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法和Thread.isAlive()的返回值等手段检测线程是否已经终止执行。

  • 线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。

  • 对象终结规则(Finalizer Rule) :一个对象的初始化完成(构造函数结束)先行发生于它的finalize()方法的开始。

  • 传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

在这里插入图片描述

共享锁

同一时间允许有多个线程去抢占这个锁

独占锁

同一时间只允许一个线程去抢占这个锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值