JUC笔记

1. JUC开发包

java.util.concurrent

1.1 并发的源头

https://www.cnblogs.com/hill1126/p/11218050.html

原子性,可见性,有序性都是缓解CPU、内存、硬盘这三者的速度差异带来的问题。

  • 原子性:多线程切换,导致线程没有按照自己的意愿正确执行;

  • 可见性:数据在缓存中的更新没有及时通知其他线程;

  • 有序性:编译器优化使程序的执行顺序发生变化。

缓存的可见性:在多核CPU中,每个核心都会有自己的独立缓存。CPU利用缓存,缓和了CPU和内存之间的速度差异带来的问题。当CPU的多个核心参与一个程序的运行,不同核心间进行各自的运算,把计算后的自己写入各自的缓存中而不写入内存,导致了每个线程都有不同的值,即缓存的可见性。

在这里插入图片描述

缓存的原子性:多线程在运行中,当一个线程对一个共享变量进行操作时,还没有执行完,就被剥夺了执行权。

2. Volatile

https://blog.csdn.net/zezezuiaiya/article/details/81456060

https://zhuanlan.zhihu.com/p/133851347

https://zhuanlan.zhihu.com/p/138819184

2.1可见性

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

Java虚拟机规范中定义了一种Java内存模型(Java Memory Model)JMM,主要目的是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存或把内存取出变量这种操作。

JMM规定所有的变量都存储在主内存(Main Memory)中,每条线程都有自己的工作内存(Work Memory),线程的工作内存中保存了从主内存中拷贝的变量副本,线程对于变量的读写都在工作内存中进行,不能直接读写主内存中的变量。本线程的工作内存的变量无法被其他线程访问,只能被主线程访问。

在这里插入图片描述

volatile定义:当对volatile变量进行写操作后,JMM会把工作内存中的最新变量强制刷新到主内存,写操作会导致其他线程中的缓存无效。

2.2 有序性

2.2.1 什么是指令重排?

为了提高性能,在遵守as-if-serial语义(不管怎么重排序,单线程下程序的执行结果不能被改变)的情况下,编译器和处理器常常会对指令进行重排序。

  1. 编译器优化重排序
  2. 指令并行重排序
  3. 内存系统重排序

在这里插入图片描述

2.2.2 禁止指令重排

volatile是通过编译器在生成字节码时,在指令序列中添加“内存屏障”来禁止指令重排序的。

硬件层面的“内存屏障”

描述
sfence:写屏障在写指令之后插入写屏障,能让写入缓存的最新数据写到主内存,保证写入的数据对其他线程可见;
ifence:读屏障在读指令之前插入读屏障,可以让高速缓存中的数据失效,重新获取主内存中的数据;
mfence:全能屏障拥有读写屏障的功能;
lock前缀一种锁,执行时会锁住内存子系统来确保执行顺序。

JVM层面的“内存屏障”

描述
LoadLoad屏障对于这样的语句,Load1,LoadLoad,Load2,在Load2读取操作要读取的数据被访问前,Load1要读取的数据必须读取完毕;
LoadStore屏障对于这样的语句,Load1,LoadStore,Store2,在Store2写入操作被刷出前,保证Local1要读取的数据被读取完毕;
StoreStore屏障对于这样的语句,Store1,StoreStore,Store2,在Store2写入操作执行前,保证Store1的写入对所有处理器可见;
StoreLoad屏障对于这样的语句,Store1,StoreLoad,Load2,在Load2读取操作执行前,保证Store1的读取对所有处理器可见;

JVM的实现会在volatile操作前后都加上内存屏障

在这里插入图片描述

2.3原子性

// Volatile与synchronized的区别
// volatile只能保证有序,可见,不能保证原子性
public class Main implements Runnable{

    static volatile int x = 1;

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++){
            x++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        Thread thread = new Thread(main);
        Thread thread1 = new Thread(main);
        thread.start();thread1.start();
        thread.join();thread1.join();
        System.out.println(x);
    }
}

// output
1532

2.4 字节码层面

public class ByteMain {
    static volatile int x = 1;

    public static void main(String[] args) {
        x = 10;
    }
}

# 反编译
# https://blog.csdn.net/Aussme/article/details/104623086
# 没有synchronized的字节码指令monitorenter和monitorexit,
$ javac ByteMain.java & javap -v ByteMain
  MD5 checksum 8b07c198eb06c81c027641ba8599d82b
  Compiled from "ByteMain.java"
public class Java.basis.volatile1.ByteMain
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
{
  static volatile int x;
    descriptor: I
    # 这里,在synchronized中,方法级的同步是隐式的,无需通过字节码指令来控制,
    # JVM可以在方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志来区分一个方法是否为同步方法,
    # volatile用访问标志ACC_VOLATILE来区分是否遵循voaltile语义
    flags: ACC_STATIC, ACC_VOLATILE

  public Java.basis.volatile1.ByteMain();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0
}

3. synchronized

https://blog.csdn.net/javazejian/article/details/72828483

https://blog.csdn.net/zwjyyy1203/article/details/106217887

https://zhuanlan.zhihu.com/p/150791816

当存在多个线程操作共享数据时,需要保证同一时刻只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行操作,这种方式叫做互斥锁。在Java中,关键字synchronized可以保证在同一时刻只有一个线程在操作共享数据,synchronized还有一个作用,可以保证一个线程的变化被其他线程看见,代替了volatile功能。

3.1 应用方式

  1. 修饰实例方法

    public class InstanceMethod implements Runnable{
    
        static int x = 0;
    
        // 当前实例方法加锁,当一个线程在访问该对象的synchronized方法时,其他线程不能访问该对象的synchronized
        // 但是如果是其他实例对象的话,操作数据是共享的,线程安全就没有保障,操作数据并非共享的,线程安全是有保障。
        public synchronized void addX(){
            x++;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                addX();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            InstanceMethod instanceMethod = new InstanceMethod();
            Thread thread = new Thread(instanceMethod);
            Thread thread1 = new Thread(instanceMethod);
            thread.start();
            thread1.start();
            thread.join();
            thread1.join();
            System.out.println(x);
        }
    }
    // output
    200000
    
  2. 修饰静态方法

    public class StaticMethod implements Runnable{
    
        static int x = 0;
    
        // 静态方法,锁的是当前类对象,x是类成员,并发操作不会发生互斥现象。
        public synchronized static void addX() {
            x++ ;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                addX();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            StaticMethod staticMethod = new StaticMethod();
            StaticMethod staticMethod1 = new StaticMethod();
            Thread thread = new Thread(staticMethod);
            Thread thread1 = new Thread(staticMethod1);
            thread.start();
            thread1.start();
            thread.join();
            thread1.join();
            System.out.println(x);
        }
    }
    
    // output
    20000
    
  3. 修饰代码块

    public class CodeBlock implements Runnable{
        static int x = 0;
    
        @Override
        public void run() {
            // class对象锁
            synchronized (CodeBlock.class) {
                for (int i = 0; i < 10000; i++){
                    x++;
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            CodeBlock codeBlock = new CodeBlock();
            CodeBlock codeBlock1 = new CodeBlock();
            Thread thread = new Thread(codeBlock);
            Thread thread1 = new Thread(codeBlock1);
            thread.start();
            thread1.start();
            thread.join();
            thread1.join();
            System.out.println(x);
        }
    }
    
    // output
    20000
    

3.2 底层语义分析

Java对象头和Monitor

在这里插入图片描述

在JVM中,对象在内存的布局分为对象头,实例数据,对齐;

  • 实例数据:存放类的属性数据信息,如果是数组的实例部分还包括数组的长度,这部分按4字节;
  • 对齐:虚拟机要求对象起始地址必须是8字节的整数倍,不够填充来补齐;
  • 对象头:虚拟机采用2个字节来存储对象头,其主要结构是由Mark Word和Class Metadata Address组成。
头对象结构说明
Mark Word存储对象的Hash,锁信息,分代年龄,GC标记等;
Class Metadata Address类型指针指向对象的元数据,虚拟机通过这个指针来确定是那个类的实例;

考虑JVM的空间效率,Mark Word被设计为一个非固定的数据结构,它会根据对象本身的状态复用自己的存储空间,如下:

在这里插入图片描述

objectMonitor(内置锁)

synchronized的对象锁,也就是重量级锁,锁标识为10,指针指向monitor的起始地址,每个对象都存在一个monitor与之关联,monitor可以与对象一起创建销毁或当线程试图获取对象的锁自动生成,monitor由ObjectMonitor实现:

// 源码在hotspot中src/share/vm/runtime/ObjectMonitor.hpp中
ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    // void *  volatile _owner;   			表示拥有该monitor的线程
    _owner        = NULL;
    // ObjectWaiter * volatile _WaitSet; 	等待线程组成的双向循环链表
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    // ObjectWaiter * volatile _cxq ;    	多线程竞争锁时的单向链表
    _cxq          = NULL ;
    FreeNext      = NULL ;
    // ObjectWaiter * volatile _EntryList ;	处于等待锁状态的线程
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
}

class ObjectWaiter : public StackObj {
 public:
  enum TStates { TS_UNDEF, TS_READY, TS_RUN, TS_WAIT, TS_ENTER, TS_CXQ } ;
  enum Sorted  { PREPEND, APPEND, SORTED } ;
  ObjectWaiter * volatile _next;
  ObjectWaiter * volatile _prev;
  Thread*       _thread;
  jlong         _notifier_tid;
  ParkEvent *   _event;
  volatile int  _notified ;
  volatile TStates TState ;
  Sorted        _Sorted ;           // List placement disposition
  bool          _active ;           // Contention monitoring is enabled
 public:
  ObjectWaiter(Thread* thread);

  void wait_reenter_begin(ObjectMonitor *mon);
  void wait_reenter_end(ObjectMonitor *mon);
};

ObjectMonitor有2个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表(每个等待锁的线程都会封装成ObjectWaiter对象), _owner指向持有ObjectMonitor对象的线程,当多线程同时访问一段同步代码时,首先进入 _EntryList,当线程获得对象的monitor后进入 _owner区域并把monitor中的 _owner变量设置为当前线程,monitor的计数器+1,若线程调用wait()方法,将释放当前的monitor, _owner变量为null,计时器-1,同时进入 _WaitSet集合等待被唤醒。若当前线程执行完毕也将释放monitor并复位变量的值,以便其他线程进入获取monitor。

在这里插入图片描述

3.3 代码块底层原理

public class Main {
    public int i;
    public void add(){
        synchronized (this){
            i++;
        }
    }
}
# 反编译
$ javac Main.java & javap -c Main
[1] 1468
Compiled from "Main.java"
public class Java.basis.concurrent.synchronized1.Main {
  public int i;

  public Java.basis.concurrent.synchronized1.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void add();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       # 进入同步方法
       # 当执行monitorenter指令时,当前线程试图获取对象锁的monitor,当对象锁的monitor的进入计数器为0,那线程可以成功取得monitor,并将计数器设置为1,
       # 如果其他线程拥有monitor的持有权,那当前线程会阻塞,直到正在执行的线程执行完毕
       3: monitorenter		
       4: aload_0
       5: dup
       6: getfield      #2                  // Field i:I
       9: iconst_1
      10: iadd
      11: putfield      #2                  // Field i:I
      14: aload_1
      # 退出同步方法
      # 线程释放monitor锁并设置计数器为0,其他线程有机会持有monitor
      15: monitorexit
      16: goto          24
      19: astore_2
      20: aload_1
      # 退出同步方法
      # 异常结束时释放被执行的monitor指令
      21: monitorexit
      22: aload_2
      23: athrow
      24: return
}

3.4 方法底层原理

public class Main {
    public int i;
    public synchronized void add(){
        i++;
    }
}
# 反编译
$ javac Main.java & javap -c Main
[3] 1478
Compiled from "Main.java"
public class Java.basis.concurrent.synchronized1.Main {
  public int i;

  public Java.basis.concurrent.synchronized1.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  # 方法级的同步是隐式的,无需通过字节码指令来控制,
  # JVM可以在方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志来区分一个方法是否为同步方法,
  # 当方法调用时,会检查ACC_SYNCHRONIZED访问标志是否被设置了,如果被设置了,执行线程先持有monitor,然后再执行方法,方法完成再释放monitor。
  # 在方法执行期间,执行线程持有monitor,其他线程都无法再获得同一个monitor,如果期间抛出异常,且无法处理异常,会在同步方法之外自动释放。
  public synchronized void add();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field i:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field i:I
      10: return
}

3.5 synchronized的优化

锁的状态有四种,无锁,偏向锁,轻量级锁,重量级锁,从低到高升级,不会降级。

偏向锁

Java6后加入的新锁,锁不仅存在多线程竞争,而且总是由同一线程连续多次获得,为了减少同一线程多次取锁(CAS)引入偏向锁。

如果一个线程获得锁,那么锁就进入偏向模式,Mark Word的结构也变为偏向锁,当这个线程再次请求锁,就无需做同步操作。

对于没有锁竞争的场合,偏向锁有很好的优化效果,因为极有可能一个线程连续多次申请相同的锁。

// 源码在hotspot中src/share/vm/runtime/synchronized.cpp中
void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {
    // 检查是否开启偏向锁
    // src/share/vm/runtime/bloabls.hpp : product(bool, UseBiasedLocking, true, "Enable biased locking in JVM")      
    if (UseBiasedLocking) {
        // 如果不处于全局安全点 ,全局安全点:在这个时间点上没有字节码正在执行
        // is_at_safepoint : inline static bool is_at_safepoint()   { return _state == _synchronized;  }
        if (!SafepointSynchronize::is_at_safepoint()) {
            // 通过revoke_and_rebias获取偏向锁
            BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);
            if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {
                return;
            }
        } else {
        // 在安全点,撤销偏向锁
            assert(!attempt_rebias, "can not rebias toward VM thread");
            BiasedLocking::revoke_at_safepoint(obj);
        }
        assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
    }
	// 不能偏向就轻量级锁
    slow_enter (obj, lock, THREAD) ;
}


// 撤销锁
void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) {
  assert(!object->mark()->has_bias_pattern(), "should not see bias pattern here");
  // if displaced header is null, the previous enter is recursive enter, no-op
  markOop dhw = lock->displaced_header();
  markOop mark ;
  if (dhw == NULL) {
     // Recursive stack-lock.
     // Diagnostics -- Could be: stack-locked, inflating, inflated.
     mark = object->mark() ;
     assert (!mark->is_neutral(), "invariant") ;
     if (mark->has_locker() && mark != markOopDesc::INFLATING()) {
        assert(THREAD->is_lock_owned((address)mark->locker()), "invariant") ;
     }
     if (mark->has_monitor()) {
        ObjectMonitor * m = mark->monitor() ;
        assert(((oop)(m->object()))->mark() == mark, "invariant") ;
        assert(m->is_entered(THREAD), "invariant") ;
     }
     return ;
  }
    
  mark = object->mark() ;

  // If the object is stack-locked by the current thread, try to
  // swing the displaced header from the box back to the mark.
  if (mark == (markOop) lock) {
     assert (dhw->is_neutral(), "invariant") ;
     if ((markOop) Atomic::cmpxchg_ptr (dhw, object->mark_addr(), mark) == mark) {
        TEVENT (fast_exit: release stacklock) ;
        return;
     }
  }

  ObjectSynchronizer::inflate(THREAD, object)->exit (true, THREAD) ;
}

revoke_and_rebias获取偏向锁流程图:

在这里插入图片描述

撤销偏向锁:

在这里插入图片描述

轻量级锁

偏向锁失败后,不会立即升级为重量级锁,成为轻量级锁。

void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {
    markOop mark = obj->mark();
    assert(!mark->has_bias_pattern(), "should not see bias pattern here");

	// 如果当前是无锁状态
	//  is_neutral() : bool is_neutral()  const { return (mask_bits(value(), biased_lock_mask_in_place) == unlocked_value); }
    if (mark->is_neutral()) {
        // 将Mark Word写入header
        lock->set_displaced_header(mark);
        // CAS操作,Mark Word更新为指向header的指针
        if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {
            // owner指向Mark Word
            TEVENT (slow_enter: release stacklock) ;
            return ;
        }
    } else 
    // mark指针是否指向当前线程的栈帧
    if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
        // 执行同步代码块
        assert(lock != mark->locker(), "must not re-lock the same lock");
        assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");
        lock->set_displaced_header(NULL);
        return;
    }

    #if 0
        if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {
            lock->set_displaced_header (NULL) ;
            return ;
        }
    #endif
      	// The object header will never be displaced to this lock,
  		// so it does not matter what the value is, except that it
  		// must be non-zero to avoid looking like a re-entrant lock,
 		// and must not look locked either.
    
    	// 自旋,防止线程被挂起,一旦可以获取资源,就直接尝试成功,如果超出阈值,还没有获取锁,那么升级为重量级锁。
    	// (自旋锁默认是10次,-XX:PreBlockSpin可以修改)
        lock->set_displaced_header(markOopDesc::unused_mark());
    	// 轻量级锁通过inflate进行膨胀升级为重量级锁
        ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);
}


// 撤销轻量级锁
void ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {
  fast_exit (object, lock, THREAD) ;
}

获取轻量级锁:

在这里插入图片描述

在这里插入图片描述

撤销轻量级锁:

在这里插入图片描述

自旋锁

轻量级锁失败后,为了避免线程在操作层面被挂起,还会进行一项称为自旋锁的优化手段。如果一直没有得到锁只能升级为重量级锁。

为什么轻量级锁还会失败?

重量级锁

一旦锁升级为重量级锁之后,就不能恢复到轻量级锁状态。当锁处于重量级锁,其他线程尝试取锁时,都会被阻塞。

3.6 synchronized的可重入性

public class ReentringDemo implements Runnable{
    static int x = 0;

    public void add(){
        x++;
    }

    @Override
    public void run() {
        synchronized (this){
            for (int i = 0; i < 100000; i++) {
                x++;
                add();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReentringDemo reentringDemo = new ReentringDemo();
        Thread thread = new Thread(reentringDemo);
        Thread thread1 = new Thread(reentringDemo);
        thread.start();thread1.start();
        thread.join();thread1.join();
        System.out.println(x);
    }
}

// output
400000

3.7线程中断

4. Locks

https://blog.csdn.net/sinat_26279177/article/details/80646301

4.1 Lock

4.1.1 Lock与synchronized的区别

  1. synchronized只能在一个方法内,Lock可以在不同的方法内;
  2. synchronized不支持公平锁,锁释放后处于等待的线程都有可能获得锁,不能指定优先级,Lock可以实现公平锁,确保等待时间最长的线程获得锁;
  3. synchronized等待中的线程不能被中断,Lock可以通过lockInterruptibly()方法中断等待的锁;
  4. synchronized不需要手动释放锁,Lock必须需要手动释放锁,没有主动释放锁,就会出现死锁的现象;
  5. Lock提供了tryLock()方法,只有在锁可用且没有其他线程使用时获得锁,减少了线程等待锁的阻塞时间。

4.1.2 Lock API

https://www.matools.com/api/java8

// 获得锁,如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获取锁。
void lock();
// 获取锁,除非当前线程是 interrupted 。获取锁,如果可用并立即返回。
void lockInterruptibly() throws InterruptedException;
// 只有在调用时才可以获得锁。
boolean tryLock();
// 如果在给定的等待时间内是空闲的,并且当前的线程尚未得到 interrupted,则获取该锁。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 返回一个新Condition绑定到该实例Lock实例
Condition newCondition();
// 为了防止死锁,必须释放锁
lock.lock();
try{}finally{
    lock.unlock();
}
// tryLock()尝试获取锁,成功返回true,失败返回false,立即返回,不会等待
// tryLock(long time, TimeUnit unit)没拿到锁会等待一定的时间。
if (lock.tryLock()) {
    try{}finally{
    	lock.unlock();
	}
} else {
    
}
// 线程获取锁,如果处于等待状态,则这个线程可以响应中断,即中断等待的线程。
// 也就是说,2个线程都通过lockInterruptibly()获取锁,线程A获得,但线程B没有获得,线程B调用interrupt()中断线程。
void lockInterruptibly() throws InterruptedException;

4.2 AQS

https://www.cnblogs.com/micrari/p/6937995.html

https://cloud.tencent.com/developer/article/1113761

http://ifeve.com/introduce-abstractqueuedsynchronizer/

https://snailclimb.gitee.io/javaguide/#/docs/java/multi-thread/AQS%E5%8E%9F%E7%90%86%E4%BB%A5%E5%8F%8AAQS%E5%90%8C%E6%AD%A5%E7%BB%84%E4%BB%B6%E6%80%BB%E7%BB%93

// 抽象队列同步器
// AQS的使用方式通常是内部类继承AQS实现同步功能
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    // 提供了一个基于FIFO队列,来管理多线程的工作,在公平竞争的情况下,无法获取同步状态的线程会被封装为一个节点,置与队列尾部。

待继续研究!!!

4.3 ReentrantLock

https://blog.csdn.net/qq_29373285/article/details/85964460

公平锁和非公平锁( 当有线程竞争锁时,当前线程会首先尝试获得锁而不是在队列中进行排队等候,这对于那些已经在队列中排队的线程来说显得不公平,这也是非公平锁的由来),默认情况下为非公平锁。

// 可重入锁,实现Lock接口
// 提供了跟synchronized方法与语句使用的隐式monitor锁
public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    
    // 默认是不公平的锁
    public ReentrantLock() {
        sync = new NonfairSync();
    }
// Sync继承了AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
    // AbstractQueuedSynchronizer继承了AOS
    // AOS主要保存获取当前锁的线程对象
    public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
// 不公平锁
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

// 公平锁
static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    final void lock() {
        acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

待继续研究!!!

4.4 ReentrantReadWriteLock

5. Atomic

原子性:多线程在运行中,当一个线程对一个共享变量进行操作时,还没有执行完,就被剥夺了执行权。

AtomicStampedReference可解决CAS发生的ABA问题。

6. Executor

在这里插入图片描述

6.1 ThreadPoolExecutor类

https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&mid=2247505103&idx=1&sn=a041dbec689cec4f1bbc99220baa7219&source=41#wechat_redirect

6.1.1构造方法

public ThreadPoolExecutor(
    					  // 核心线程数,最小可以同时运行的线程数量					  
    					  int corePoolSize,
    				      // 当队列中的任务达到队列的容量时,当前可以同时运行的线程数量变为最大线程数
                          int maximumPoolSize,
    					  // 当线程数量超过核心线程数时,多余的空闲线程存活的最长时间
                          long keepAliveTime,
    					  // keepAliveTime单位
                          TimeUnit unit,
    					  // 当前线程数量达到核心线程数时,新任务会存放在队列中
                          BlockingQueue<Runnable> workQueue,
    					  // 线程工厂,创建线程
                          ThreadFactory threadFactory,
    					  // 拒绝策略
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

// 线程工厂,默认是Executor的defaultThreadFactory()
public static ThreadFactory defaultThreadFactory() {
    return new DefaultThreadFactory();
}

在这里插入图片描述

6.1.2 RejectedExecutionHandler拒绝策略

当前同时运行的线程数量达到最大限制且队列也放满了任务,会执行拒绝策略。

ThreadPoolExecutor的拒绝策略常用的是DiscardPolicy或者DiscardOldestPolicy。

// 不会被丢弃,会调用executor方法的线程来执行被拒绝的任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

// 抛出异常,拒绝处理
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}


// 不处理,直接丢弃
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

// 丢弃最早未处理的
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

7.CAS

CAS的ABA问题:第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值 A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以 compareAndSet 操作是成功。

// ConcurrentHashMap的iniTable的compareAndSwapInt
// https://blog.csdn.net/aileitianshi/article/details/108844586

private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0)
            Thread.yield(); // lost initialization race; just spin
        // CAS比较替换,乐观锁,如果内存值和旧值相同,则将内存值替换为待修改的值
        // SIZECTL(Unsafe.objectFieldOffset获得,指内存偏移值)内存值与sc旧的预期值比较 -1为待修改的值
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
                }
            } finally {
                sizeCtl = sc;
            }
            break;
        }
    }
    return tab;
}


UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  // 根据操作的对象和其在内存中的offset,计算出内存中具体位置
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  // 这里判断是否交换成功,相同则交换成功,不同则交换失败
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
UNSAFE_END
    

jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
  assert(sizeof(jbyte) == 1, "assumption.");
  uintptr_t dest_addr = (uintptr_t)dest;
  uintptr_t offset = dest_addr % sizeof(jint);
  volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
  jint cur = *dest_int;
  jbyte* cur_as_bytes = (jbyte*)(&cur);
  jint new_val = cur;
  jbyte* new_val_as_bytes = (jbyte*)(&new_val);
  // 指向new_val_as_bytes的值覆盖为exchange_value,new_val也变为exchange_value
  new_val_as_bytes[offset] = exchange_value;
  // 如果当前值和期望值不相同,直接返回cur_as_bytes[offset],上层方法会判断交换失败
  // 相同的话做交换操作
  while (cur_as_bytes[offset] == compare_value) {
    // 尝试交换,调用机器指令,返回的是当前地址上的值即最新的值
    // new_val内存中的值,
    jint res = cmpxchg(new_val, dest_int, cur);
    if (res == cur) break;
    // 失败时说明有竞争, 其他线程先行改变了原来在内存里的值, 则需要继续重试
    // cur改变为最新的值
    // cur_as_bytes[offset]也改变为最新值  
    cur = res;
    new_val = cur;
    new_val_as_bytes[offset] = exchange_value;
  }
  // 有竞争时,cur_as_bytes[offset]为最新在内存中的值
  return cur_as_bytes[offset];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值