JUC-八股-01

什么叫进程?什么叫线程?

进程: 简单点讲叫做代码在数据集合上的一次运行活动

线程: 进程里面最小的执行单元,简单讲一个线程里面不同的执行路径

创建线程的方式?

本质只有一种: 通过Thread类进行创建

多种形式

  1. 继承Thread类重写run() 方法
  2. 实现Runnable接口实现run() 方法
  3. 实现Callable + FutureTask方式
  4. 交给线程池创建

线程状态

alt alt

synchronized

访问某一段临界代码或者临界资源的时候需要一把锁,synchronized就是我们最常用的锁。

synchronized 保证原子性,保证可见性

并且他是可重入的

1.它的实现

  1. 字节码层面

    方法 ACC_SYNCHRONIZED

    代码块 monitorenter 和 monitorexit指令

    public class TestSync {
        void n() {
            synchronized (this) {
    
            }
        }
        public static void main(String[] args) {
        }
    }
    
    

alt

为什么两次monitorexit呢?第一个是正常退出,第二个是遇到异常自动退出。

  1. jvm层面

    用c和c++调用操作系统提供的同步机制,不同操作系统不一样。

  2. os和硬件

    c和c++调用操作系统提供的同步机制, 这些同步机制依赖于硬件

    x86 lock cmpxchg xxx 类似cas

2.锁升级的概念

synchronized以前是重量级锁, 因为申请锁资源必须通过kernel, 需要操作系统参与,有点消耗资源。用户态 -> 内核态

用户态->内核态
int 0x80 -   128
保护用户态现场
寄存器压栈
    
进行syscall
内核态返回eax
恢复用户态现场
用户程序继续执行

后来经过改进有了锁升级的概念

alt

new - 偏向锁 - 轻量级锁 (无锁, 自旋锁,自适应自旋)- 重量级锁

偏向锁:记录这个线程的Id (对象头markword)

自旋锁: 如果线程争用,就升级为自旋锁(线程数量少)

自选锁转 10 次 升级成重量级锁(线程数量多)

为什么有自旋锁还需要重量级锁?

自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗

重量级锁有等待队列,所有拿不到锁的进入等待队列,不需要消耗CPU资源

偏向锁是否一定比自旋锁效率高?

不一定,在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销,这时候直接使用自旋锁

JVM启动过程,会有很多线程竞争(明确),所以默认情况启动时不打开偏向锁,过一段儿时间再打开

推荐两篇文章

https://www.jianshu.com/p/b43b7bf5e052

https://www.jianshu.com/p/16c8b3707436

synchronized vs Lock (CAS)

在高争用 高耗时的环境下synchronized效率更高
 在低争用 低耗时的环境下CAS效率更高
 synchronized到重量级之后是等待队列(不消耗CPU)
 CAS(等待期间消耗CPU)
 
 一切以实测为准

Volatile 关键字

使一个变量在多个线程之间可见

保证线程可见性

​ 缓存一致性协议

​ x86 MESI

禁止指令重排序

不保证原子性: 不能替代synchronized

1. 它的实现

1.字节码层面

ACC_VOLATILE

2.jvm层面

jvm层级的内存屏障

StoreStoreBarrier
volatile写操作
StoreLoadBarrier

LoadLoadBarrier
volatile读操作
LoadStoreBarrier

操作系统级的内存屏障

sfence 它之前的写操作必须必在后面写操作之前完成
lfence ...读...
mfence  ...读写..
  1. os和硬件层面

windows 的lock指令

linux 上实现是 上面一个屏障 下面一个屏障 最后一个lock指令

中间是volatile区域

Java内存模型

https://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html alt

缓存一致性协议

https://www.cnblogs.com/z00377750/p/9180644.html

双重检查单例模式,我们需要给instance成员变量加上volatile关键字,禁止指令重排序才行。实际上,只有很低版本的Java有这个问题。我们现在用的高版本的Java已经在JDK内部实现中解决了这个问题(解决的方法很简单,只要把对象new操作和初始化操作设计为原子操作,就自然能禁止重排序)。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务