java并发关键知识速记

JVM运行时数据区域

JVM栈、native方法栈、程序计数器、堆(字符串常量池)、元空间(运行时常量池,包含类信息、常量、静态变量)

JMM

内存不可见与线程间通信
重排序(编译器、指令、内存)
遵守as-if-serial原则(数据依赖性)
内存屏障指令
happens-before关系、规则

如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见

锁机制

对象头–MarkWord–线程id+四种锁类型、锁升级的条件

ThreadLocal

同步的措施一般是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担。
那么有没有一种方式可以做到,当创建一个变量后,每个线程对其进行访问的时候访问的是自己线程的变量呢?其实ThreadLocal就可以做这件事情
ThreadLocalget(),set()的时候都会清除线程ThreadLocalMap里所有key为null的value。
ThreadLocalremove()方法会先将Entry中对key的弱引用断开,设置为null,然后再清除对应的key为null的value。
remove()方法的关键就在于主动断开entry的key的引用链接,这样在后续的expungeStaleEntry()方法中,就会将这种key为null的entry给设置为null,方便GC对内存进行回收。

CAS

四个数据:对象内存+属性的偏移量+预期值+新值

Unsafe

提供硬件级别的CASjavac–javap得到c++代码–发现汇编指令cmpxchg–硬件级别的原子操作)、获取内存中对象的属性的偏移量、操作内存中对象、挂起唤醒线程、获取内存信息、复制内存

程序会根据当前处理器的类型来决定是否为 cmpxchg 指令添加 lock 前缀。如果程序是在多处理器上运行,就为 cmpxchg 指令加上 lock 前缀(lock cmpxchg)。反之,如果程序是在单处理器上运行,就省略 lock 前缀(单处理器自身会维护单处理器内的顺序一致性,不需要 lock 前缀提供的内存屏障效果)。

AtomicInteger

属性:volatile修饰的valueunsafevalueOffset

Random

属性:AtomicLong类型的seed

	protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed)); //while循环调用seed的cas方法
        return (int)(nextseed >>> (48 - bits));
    }

ThreadLocalRandom

public class ThreadLocalRandom extends Random
属性:AtomicInteger类型的probeGeneratorAtomicLong类型的seeder(new创建时参数传入initialSeed()

	private static final sun.misc.Unsafe UNSAFE;
	private static final long SEED; // Thread#threadLocalRandomSeed的偏移量
    private static final long PROBE; // Thread#threadLocalRandomProbe的偏移量
    private static final long SECONDARY; // Thread#threadLocalRandomSecondarySeed的偏移量
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> tk = Thread.class;
            SEED = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSeed"));
            PROBE = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomProbe"));
            SECONDARY = UNSAFE.objectFieldOffset
                (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

	static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
    }

	final long nextSeed() {
        Thread t; long r; // read and update per-thread seed
        UNSAFE.putLong(t = Thread.currentThread(), SEED, r = UNSAFE.getLong(t, SEED) + GAMMA);
        return r;
    }

ThreadLocalRandom中并没有存放具体的种子,具体的种子存放在具体的调用线程的threadLocalRandomSeed变量里面。ThreadLocalRandom类似于ThreadLocal类,就是个工具类,注意:它并没有使用ThreadLocal

当线程调用ThreadLocalRandomcurrent()方法时,ThreadLocalRandom负责初始化调用线程的threadLocalRandomSeed变量,也就是初始化种子。

当调用ThreadLocalRandomnextInt()方法时,实际上是获取当前线程的threadLocalRandomSeed变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed变量,而后再根据新种子并使用具体算法计算随机数。

其中seederprobeGenerator是两个原子性变量,在初始化调用线程的种子和探针变量时会用到它们,每个线程只会使用一次。

变量instanceThreadLocalRandom的一个实例,该变量是static的。当多线程通过ThreadLocalRandomcurrent()方法获取ThreadLocalRandom的实例时,其实获取的是同一个实例。

LockSupport

AQS

Node#waitStatus:

  • 1:CANCELLED,表示取消排队了
  • 0:默认值,表示在正常排队
  • -1:SIGNAL,表示释放锁后需要唤醒下一个waitStatus<=0的节点
  • -2:CONDITION,表示在条件队列上排队
  • -3:PROPAGATE

执行流程:

  • acquire(int arg)
    • tryAcquire(),子类实现,通过CAS改变state值
    • addWaiter()入队
      • 尝试直接CAS快速入队(避免大多数情况下的多余判空,见后面的enq)
      • 尝试失败,就执行enq(),比快速入队多了判断pre=null的操作
      • return node
    • acquireQueue(node, arg)排队
      • 若preNode为head,则尝试再次tryAcquire(),若成功则设置当前node为head,设置preNode.next=nul(帮助GC),再return。
      • 若尝试失败或preNode不是head,则先找到安全点:往前找第一个waitStatus小于等于0的node,设置它的waitStatus为Signal,再park挂起自己
      • 若能执行到这里,说明被unpark唤醒了,返回Thread.interrepted()是为了知道在挂起期间是否被其他线程调用过interrept(),外层便可判断:若是,就执行Thread.current().interrept()来响应中断请求!(响应延迟了哦)
  • release(int arg)
    • tryRelease(),子类实现,通过CAS改变state值
    • 获取head-Node
    • 若head为null或head的waitStatus!=0,则unparkSuccessor()
      • 若head的waitStatus<0(即可能需要信号),则通过CAS置为默认值0(即清除等待信号),因为它使命完成了,成为用来占位的哨兵节点
      • 找head的后继节点
        • 如果后继节点为null或者后继节点的waitStatus>0,从tail开始往前找(为什么)第一个waitStatus<=0的节点,并unpark它
        • 否则,unpark后继节点
    • return true

ReentrantLock

非公平锁的tryAcquire()

int c = getState();
 //(4)当前AQS状态值为0
 if (c == 0) {
 	// 直接cas获取锁
    if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
 }

公平锁的tryAcquire()

int c = getState();
//(7)当前AQS状态值为0
if (c == 0) {
	//(8)公平性策略
    if (! hasQueuedPredecessors() &&
        compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
}

CountDownLatch

ConcurrentHahMap

ThreadPoolExecutor

AtomicInteger类型的ctl,高三位表示线程池状态runState,其余低位表示有效的工作线程数workerCount
内部类Worker:private final class Worker extends AbstractQueuedSynchronizer implements Runnable,是一个简单实现的不可重入锁。

并发集合

CopyOnWriteArrayList

并发队列

ConcurrentLinkedQueue

关键属性:

  • head: Node

注意: 这里的Node有属性item、next、unsafe、itemOfffset、nextOffset,并提供有方法casItem()和casNext()

  • tail: Node

LinkedBlockingQueue

关键属性:

  • head: Node
  • last: Node
  • count: AtomicInteger
  • takeLock: ReentrantLock、
  • notEmpty: Condition
  • putLock: ReentrantLock、
  • notFull: Condition
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值