变量与线程安全

volatile关键字

考虑可见性,不考虑原子性(变量级别)

作用:private volatile int a = 0;

强制线程到共享内存中读取数据,而不从线程工作内存中读取,从而使变量在多个线程间可见。

public class DemoThread13{
   
   private List<String> list = new ArrayList<String>();
   private /*volatile*/ boolean canGet = false;
   
   public void put(){
      for(int i=0;i<10;i++){
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
         list.add("A");
         System.out.println("线程"+Thread.currentThread().getName()+"添加第"+i+"个元素");
         if(i==5){
            //循环到第次则通知其他线程开始获取数据进行处理
            canGet = true;
            System.out.println("线程"+Thread.currentThread().getName()+"发出通知");
         }
      }
   }
   
   public void get(){
      while(true){
         if(canGet){
            for(String s:list){
               System.out.println("线程"+Thread.currentThread().getName()+"获取元素:"+s);
            }
            break;
         }
      }
   }
   
   public static void main(String[] args) {
      
      final DemoThread13 demo = new DemoThread13();
      
      new Thread(new Runnable() {
         @Override
         public void run() {
            demo.put();
         }
      },"t1").start();;
      
      new Thread(new Runnable() {
         @Override
         public void run() {
            demo.get();
         }
      },"t2").start();
      
   }

   
}

执行结果,canGet对线程 不可见

线程t1添加第0个元素
线程t1添加第1个元素
线程t1添加第2个元素
线程t1添加第3个元素
线程t1添加第4个元素
线程t1添加第5个元素
线程t1发出通知
线程t1添加第6个元素
线程t1添加第7个元素
线程t1添加第8个元素
线程t1添加第9个元素

canGet加上关键字volatile

线程t1添加第0个元素
线程t1添加第1个元素
线程t1添加第2个元素
线程t1添加第3个元素
线程t1添加第4个元素
线程t1添加第5个元素
线程t1发出通知
线程t2获取元素:A
线程t2获取元素:A
线程t2获取元素:A
线程t2获取元素:A
线程t2获取元素:A
线程t2获取元素:A
线程t1添加第6个元素
线程t1添加第7个元素
线程t1添加第8个元素
线程t1添加第9个元素

volatile不能保证原子性

public class DemoThread14 implements Runnable{
   
// public static /*volatile*/ AtomicInteger sum = new AtomicInteger(0);
   public static volatile int sum = 0;

   public static void add(){
      System.out.println(Thread.currentThread().getName()+"初始sum="+sum);
      for(int i=0;i<10000;i++){
         sum++;
//       sum.addAndGet(1);
      }
      System.out.println(Thread.currentThread().getName()+"计算后sum="+sum);
   }
   
   @Override
   public void run() {
      add();
   }
   
   public static void main(String[] args) {
      //如果volatile具有原子性,那么10个线程并发调用,最终结果应该为100000
      ExecutorService es = Executors.newFixedThreadPool(10);
      for(int i=0;i<10;i++){
         es.submit(new DemoThread14());
      }
      es.shutdown();
      while(true){
         if(es.isTerminated()){
            System.out.println("sum最终="+sum);
            if(sum==100000){
               System.out.println(sum+"=ok");
            }else{
               System.out.println(sum+"=no");
            }
            break;
         }
      }
   }
}

执行结果

pool-1-thread-1初始sum=0
pool-1-thread-7初始sum=0
pool-1-thread-6初始sum=0
pool-1-thread-5初始sum=0
pool-1-thread-2初始sum=0
pool-1-thread-3初始sum=0
pool-1-thread-4初始sum=0
pool-1-thread-7计算后sum=13938
pool-1-thread-6计算后sum=16003
pool-1-thread-9初始sum=4468
pool-1-thread-3计算后sum=20514
pool-1-thread-4计算后sum=21974
pool-1-thread-2计算后sum=24317
pool-1-thread-8初始sum=3642
pool-1-thread-5计算后sum=7841
pool-1-thread-9计算后sum=14328
pool-1-thread-8计算后sum=18233
pool-1-thread-10初始sum=13138
pool-1-thread-1计算后sum=18915
pool-1-thread-10计算后sum=26227
sum最终=26227
26227=no

volatile关键字和static的区别

static变量可以被多个实例共享,针对的是类和实例,在多线程情况下和非静态成员是一致的

Static保证唯一性, 不保证一致性,多个实例共享一个静态变量。

Volatile保证一致性,不保证唯一性(不被多个实例共享),多个实例有多个volatile变量。

Atomic类

不考虑可见性,考虑原子性(变量级别),AtomicXXX类不能保证成员方法的原子性

public class DemoThread15 implements Runnable{
   
   //原子类
   private static AtomicInteger sum = new AtomicInteger(0);
   
   public static void add(){
      for(int i=0;i<10000;i++){
         sum.addAndGet(1);
      }
      if(sum.intValue()==100000){
         System.out.println(sum+"=ok");
      }else{
         System.out.println(sum+"=no");
      }
   }
   
   @Override
   public void run() {
      add();
   }
   
   public static void main(String[] args) {
      //10个线程调用,最终结果应该为100000
      ExecutorService es = Executors.newFixedThreadPool(10);
      for(int i=0;i<10;i++){
         es.submit(new DemoThread15());
      }
      es.shutdown();
   }
}

不能保证方法的原子性

public class DemoThread16 implements Runnable{
   
   //原子类
   private static AtomicInteger sum = new AtomicInteger(0);
   
   //如果add方法是原子性的,那么每次的结果都是10的整数倍
   /*synchronized*/
   public /*synchronized*/ static void add(){
      sum.addAndGet(1);
      try {
         Thread.sleep(1000);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      sum.addAndGet(9);
      System.out.println(sum);
   }
   
   @Override
   public void run() {
      add();
   }
   
   public static void main(String[] args) {
      //10个线程调用,每个线程得到10的倍数, 最终结果应该为100,才是正确的
      ExecutorService es = Executors.newFixedThreadPool(10);
      for(int i=0;i<10;i++){
         es.submit(new DemoThread16());
      }
      es.shutdown();
   }

   
}

运行结果,10个线程调用,每个线程得到10的倍数, 最终结果应该为100,才是正确的

37
37
37
55
46
100
82
73
64
91

AtomicInteger的addAndGet使用了CAS(compare and swap,JDK7),只有数据的版本号和指定版本号一致才修改

CAS原理

synchronized解决了原子性和可见性的问题,但是会造成频繁的上下文切换,造成性能开销

  1. JDK提供的非阻塞原子操作,通过硬件保证了比较、更新操作的原子性
  2. JDK的Unsafe类提供了一系列的compareAndSwap*方法来支持CAS操作

本质上是乐观锁,下图是原理

image-20210225130036178

CAS-ABA问题

image-20210225130502562

  1. 如果程序按照1~5的顺序执行,依然是成功的,然而线程1修改时x的值时其实已经从x= A =>B =>A。
  2. 由于变量的值产生了环形转换,从A变为B又变回了A。如果不存在环形转换也就不存在ABA问题。

使用版本号可以解决唤醒转换的问题

  1. 给变量分配时间戳(版本)、版本来解决ABA问题
  2. JDK中使用java.util.concurrent.atomic.AtomicStampedReference类给每个变量的状态都分配一个时间 戳,避免ABA问题产生。
public class CasDemo0 {

    //设置初始值和时间戳
    private static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(100, 0);

    public static void main(String[] args) throws InterruptedException {
        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    //修改值和时间戳
                    boolean sucess = atomic.compareAndSet(100, 101, atomic.getStamp(), atomic.getStamp() + 1);
                    System.out.println(Thread.currentThread().getName()+" set 100>101 : "+sucess);
                    sucess = atomic.compareAndSet(101, 100, atomic.getStamp(), atomic.getStamp() + 1);
                    System.out.println(Thread.currentThread().getName()+" set 101>100 : "+sucess);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t0.start();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    int stamp = atomic.getStamp();
                    System.out.println(Thread.currentThread().getName()+" 修改之前 : " +stamp);
                    TimeUnit.SECONDS.sleep(2);
                    int stamp1 = atomic.getStamp();
                    System.out.println(Thread.currentThread().getName()+" 等待两秒之后,版本被t0线程修改为 : " +stamp1);

                    //提醒: 一下两次修改都不会成功,因为版本不符,虽然期待值是相同的,因此解决了ABA问题
                    boolean success = atomic.compareAndSet(100, 101, stamp, stamp + 1);
                    System.out.println(Thread.currentThread().getName()+" set 100>101 使用错误的时间戳: " + success);
                    success = atomic.compareAndSet(101,100,stamp,stamp+1);
                    System.out.println(Thread.currentThread().getName()+" set 101>100 使用错误的时间戳: " + success);

                    //提醒:以下修改是成功的,因为使用了正确的版本号,正确的期待值
                    success = atomic.compareAndSet(100,101,stamp1,stamp1+1);
                    System.out.println(Thread.currentThread().getName()+" set 100>101 使用正确的时间戳: " + success);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();

        t0.join();
        t1.join();

        System.out.println("main is over");
    }
}

执行结果

Thread-1 修改之前 : 0
Thread-0 set 100>101 : true
Thread-0 set 101>100 : true
Thread-1 等待两秒之后,版本被t0线程修改为 : 2
Thread-1 set 100>101 使用错误的时间戳: false
Thread-1 set 101>100 使用错误的时间戳: false
Thread-1 set 100>101 使用正确的时间戳: true
main is over

查看AtomicStampedReference源码,类中声明类静态类,类中的get和set操作都是针对Pair

private static class Pair<T> {
    final T reference;
    final int stamp;
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    static <T> Pair<T> of(T reference, int stamp) {
        return new Pair<T>(reference, stamp);
    }
}

private volatile Pair<V> pair;

/**
 * Creates a new {@code AtomicStampedReference} with the given
 * initial values.
 *
 * @param initialRef the initial reference
 * @param initialStamp the initial stamp
 */
public AtomicStampedReference(V initialRef, int initialStamp) {
    pair = Pair.of(initialRef, initialStamp);
}

ThreadLocal原理

使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本

public class DemoThread21 {
   public static void main(String[] args) throws InterruptedException {
      
      final ThreadLocal<Integer> th = new ThreadLocal<Integer>();
      
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               th.set(100);
               System.out.println("t1 set th="+th.get());
               Thread.sleep(2000);
               System.out.println("t1 get th="+th.get());
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }).start();
      
      Thread.sleep(1000);
      
      new Thread(new Runnable() {
         @Override
         public void run() {
            Integer ele = th.get();
            System.out.println("t2 get th="+ele);
            th.set(200);
            System.out.println("t2 get th="+th.get());
         }
      }).start();
      
   }
}

执行结果,可以看出两个线程使用的数据是各自的副本

t1 set th=100
t2 get th=null
t2 get th=200
t1 get th=100

ThreadLocal源码解析

1. Thread类中的threadLocals、inheritableThreadLocals成员变量为ThreadLocal.ThreadLocalMap对象
2. Map的key值是ThreadLocal对象本身,查看set、set、remove方法
3. Thread无法解决继承问题,而InheritableThreadLocal可以
4. InheritableThreadLocal继承自ThreadLocal
5. InheritableThreadLocal可以帮助我们做链路追踪

本质上是一个Map,在Thread中查看,get和set方法首先从当前线程获取ThreadLocalMap,获取不到就创建

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

ThreadLocalMap是ThreadLocal类中的静态内部类,以下是ThreadLocalMap类内部

/**
 * The entries in this hash map extend WeakReference, using
 * its main ref field as the key (which is always a
 * ThreadLocal object).  Note that null keys (i.e. entry.get()
 * == null) mean that the key is no longer referenced, so the
 * entry can be expunged from table.  Such entries are referred to
 * as "stale entries" in the code that follows.
 */
//键值对内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

/**
 * The initial capacity -- MUST be a power of two.
 */
//初始容量
private static final int INITIAL_CAPACITY = 16;

/**
 * The table, resized as necessary.
 * table.length MUST always be a power of two.
 */
//本质上是键值对数组
private Entry[] table;

构造函数,寻址算法跟HashMap类似

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
    table = new Entry[INITIAL_CAPACITY];
    int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
    table[i] = new Entry(firstKey, firstValue);
    size = 1;
    setThreshold(INITIAL_CAPACITY);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
    //复制传入的Map的参数
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    //将存储数组的值复制过来
    for (int j = 0; j < len; j++) {
        Entry e = parentTable[j];
        if (e != null) {
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                Object value = key.childValue(e.value);
                Entry c = new Entry(key, value);
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            }
        }
    }
}

ThreadLocal的get方法

/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //如果为空就设置初始值
    return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

设置初始值

/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue() {
    //获取null初始值
    T value = initialValue();
    Thread t = Thread.currentThread();
    //获取ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        //创建map
        createMap(t, value);
    return value;
}

创建map

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocal的set方法

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

createMap方法如上

例子

查看源码进行分析,只是在main线程中创建了ThreadLocalMap

public class ThreadLocalDemo0 {

    public static ThreadLocal<String> tl = new ThreadLocal<>();

    public static void main(String[] args) {
        //查看源码进行分析,只是在main线程中创建了ThreadLocalMap
        tl.set("寒假太短了");

        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                //子线程无法获取父线程ThreadLocal的值,因为他们是两个独立的用户线程,都使用各自的副本
                //获取到的ThreadLocalMap是空的,调用setInitialValue
                System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
            }
        });

        t0.start();

        //主线程可以获取到
        System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
    }

}

运行结果,主线程设置的ThreadLocal的值子线程获取不到

main get tl is : 寒假太短了
Thread-0 get tl is : null

InheritableThreadLocal

public class ThreadLocalDemo1 extends Thread{

    //多个线程之间读取副本, 父子线程之间复制传递
    public static InheritableThreadLocal<String> tl = new InheritableThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {

        tl.set("寒假太短了");

        Thread t0 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
                tl.set("After Set The Value Change to " + Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
            }
        },"t1");

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
                tl.set("After Set The Value Change to " + Thread.currentThread().getName());
                System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
            }
        },"t2");

        t0.start();

        Thread.sleep(1000);

        t1.start();

        System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
    }

}

执行结果,在子线程中设置值,并没有相互影响各自的副本

t1 get tl is : 寒假太短了
t1 get tl is : After Set The Value Change to t1
main get tl is : 寒假太短了
t2 get tl is : 寒假太短了
t2 get tl is : After Set The Value Change to t2

作用:链路追踪,比如操作用户id的时候是线程套线程的,很难跟踪执行的路径,可以通过父子线程传值的方式解决问题

public class ThreadLocalDemo2 extends Thread{

    //多个线程之间读取副本, 父子线程之间复制传递
    public static InheritableThreadLocal<Integer> tl = new InheritableThreadLocal<>();

    public ThreadLocalDemo2(){
    }

    public void run(){
        System.out.println(Thread.currentThread().getName()+ " get tl is : " + tl.get());
        new ThreadLocalDemo2().start();
    }

    public static void main(String[] args) throws InterruptedException {
        tl.set(1001);
        new ThreadLocalDemo2().start();

        tl.set(2002);
        new ThreadLocalDemo2().start();

    }

}

输出结果,如此可以看出线程的执行情况

Thread-1 get tl is : 2002
Thread-0 get tl is : 1001
Thread-2 get tl is : 2002
Thread-3 get tl is : 1001
Thread-4 get tl is : 2002
Thread-5 get tl is : 1001
Thread-6 get tl is : 2002
Thread-7 get tl is : 1001
Thread-8 get tl is : 2002
Thread-9 get tl is : 1001
Thread-10 get tl is : 2002

源码

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

只看这几个函数,InheritableThreadLocal和ThreadLocal是一样的,父子线程不共享数据

查看Thread类中的初始函数

构造函数

/**
 * Allocates a new {@code Thread} object. This constructor has the same
 * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
 * {@code (null, target, gname)}, where {@code gname} is a newly generated
 * name. Automatically generated names are of the form
 * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
 *
 * @param  target
 *         the object whose {@code run} method is invoked when this thread
 *         is started. If {@code null}, this classes {@code run} method does
 *         nothing.
 */
public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}
/**
 * Initializes a Thread with the current AccessControlContext.
 * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext,boolean)
 */
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals//是否初始化这个值,传入为true
                 ) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    //得到父线程
    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

        /* If there is a security manager, ask the security manager
           what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn't have a strong opinion of the matter
           use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
       explicitly passed in. */
    g.checkAccess();

    /*
     * Do we have the required permissions?
     */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    //子线程创建InheritedMap
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}

创建InheritedMap

/**
 * Factory method to create map of inherited thread locals.
 * Designed to be called only from Thread constructor.
 *
 * @param  parentMap the map associated with parent thread
 * @return a map containing the parent's inheritable bindings
 */
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}

在创建线程的时候就检查是否有可继承的inheritableThreadLocals

Unsafe类

Atomic类大量使用了Unsafe类

查看AtomicLong源码

/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

查看getAndAddLong

public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}

调用本地方法,底层实现的原子性操作方法(硬件实现)

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

Unsafe类安全限定

地址

image-20210225151934776

典型的单例方法

private static final Unsafe theUnsafe;
private Unsafe() {
}

@CallerSensitive
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}
例子
public class UnsafeDemo0 {

    private int age;

    public int getAge() {
        return age;
    }

    public static void main(String[] args) {
        UnsafeDemo0 demo0 = new UnsafeDemo0();

        // 获取Unsafe类实例
        Unsafe unsafe = Unsafe.getUnsafe();
        try {
            //获取age属性的内存偏移地址
            long ageOffset = unsafe.objectFieldOffset(UnsafeDemo0.class.getDeclaredField("age"));
            //设置age的值为11
            unsafe.putInt(demo0,ageOffset,11);
            //输出结果
            System.out.println(demo0.getAge());
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

运行结果,自己写的类是由AppClassLoader加载的,不是由BootstropClassLoader加载的,所以报错(限制了使用,rt.jar包中可调用)

Exception in thread "main" java.lang.SecurityException: Unsafe
	at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
	at com.mkevin.demo3.UnsafeDemo0.main(UnsafeDemo0.java:20)
[ERROR] Command execution failed.
org.apache.commons.exec.ExecuteException: Process exited with an error: 1 (Exit value: 1)
    at org.apache.commons.exec.DefaultExecutor.executeInternal (DefaultExecutor.java:404)
    at org.apache.commons.exec.DefaultExecutor.execute (DefaultExecutor.java:166)

查看源码objectFieldOffset

/**
     * Reports the location of a given field in the storage allocation of its
     * class.  Do not expect to perform any sort of arithmetic on this offset;
     * it is just a cookie which is passed to the unsafe heap memory accessors.
     *
     * <p>Any given field will always have the same offset and base, and no
     * two distinct fields of the same class will ever have the same offset
     * and base.
     *
     * <p>As of 1.4.1, offsets for fields are represented as long values,
     * although the Sun JVM does not use the most significant 32 bits.
     * However, JVM implementations which store static fields at absolute
     * addresses can use long offsets and null base pointers to express
     * the field locations in a form usable by {@link #getInt(Object,long)}.
     * Therefore, code which will be ported to such JVMs on 64-bit platforms
     * must preserve all bits of static field offsets.
     * @see #getInt(Object, long)
     */
    @ForceInline
    public long objectFieldOffset(Field f) {
        if (f == null) {
            throw new NullPointerException();
        }
        Class<?> declaringClass = f.getDeclaringClass();
        if (declaringClass.isHidden()) {
            throw new UnsupportedOperationException("can't get field offset on a hidden class: " + f);
        }
        if (declaringClass.isRecord()) {
            throw new UnsupportedOperationException("can't get field offset on a record class: " + f);
        }
        return theInternalUnsafe.objectFieldOffset(f);
    }

Unsafe类的put和get方法

地址类操作

1. objectFieldOffset 获取字段偏移地址
2. staticFieldOffset 获取静态字段偏移地址
3. arrayBaseOffset 获取数组中第一个元素的地址
4. arrayIndexScale 获取数组中一个元素占用的字节

get和put

1. getInt、getLong、getBoolean、getChar、getFloat、getByte、getDouble、getObject 获取字段值
2. putInt、putLong、putBoolean、putChar、putFloat、putByte、putDouble、putObject 设置字段值
3. 直接操作内存地址 如public double getDouble(long address)
4. 通过对象内存地址操作 如public double getDouble(Object o, long offset)

参数为int不推荐

/** @deprecated */
@Deprecated
public double getDouble(Object var1, int var2) {
    return this.getDouble(var1, (long)var2);
}

volatile类操作

1. getIntVolatile、getLongVolatile、getBooleanVolatile、getObjectVolatile、getByteVolatile、
getShortVolatile、getCharVolatile、getFloatVolatile、getDoubleVolatile
获取volatile字段值,保证可见性
2. putIntVolatile、putLongVolatile、putBooleanVolatile、putObjectVolatile、putByteVolatile、
putShortVolatile、putCharVolatile、putFloatVolatile、putDoubleVolatile
设置volatile字段值,保证可见性

and类操作

1. putOrderedInt、 putOrderedLong、 putOrderedObject 保证顺序性、具有lazy特性、不保证可见性
2. getAndSetInt、getAndSetLong、getAndSetObject 自旋操作、先获取后设置
3. getAndAddInt、getAndAddLong 自旋操作、先获取后设置
4. compareAndSwapInt、compareAndSwapLong、compareAndSwapObject CAS相关操作

内存操作

1. public native long allocateMemory(long bytes); 分配内存
2. public native long reallocateMemory(long address, long bytes); 重新分配内存
3. public native void setMemory(Object o, long offset, long bytes, byte value);
4. public void setMemory(long address, long bytes, byte value) 初始化内存
5. public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset,
long bytes);
6. public void copyMemory(long srcAddress, long destAddress, long bytes) 复制内存
7. public native void freeMemory(long address);
public class UnsafeDemo1 {

    private int age;

    public int getAge() {
        return age;
    }

    public static void main(String[] args) {
        UnsafeDemo1 demo0 = new UnsafeDemo1();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            //获取age属性的内存偏移地址
            long ageOffset = unsafe.objectFieldOffset(UnsafeDemo1.class.getDeclaredField("age"));
            //设置age的值为11
            unsafe.putInt(demo0,ageOffset,11);
            System.out.println("KEVIN-1>>"+demo0.getAge());
            //获取age
            int age = unsafe.getInt(demo0,ageOffset);
            System.out.println("KEVIN-1-getInt>>"+age);
            //验证CAS方法 预期值10 改为50
            boolean result = unsafe.compareAndSwapInt(demo0,ageOffset,10,50);
            System.out.println("KEVIN-2>>"+demo0.getAge()+","+result);
            //验证CAS方法
            result = unsafe.compareAndSwapInt(demo0,ageOffset,11,100);
            System.out.println("KEVIN-3>>"+demo0.getAge()+","+result);
            //获取并设置
            age = unsafe.getAndSetInt(demo0,ageOffset,99);
            System.out.println("KEVIN-4-getAndSetInt>>"+age+","+demo0.getAge());
            //获取并增加
            age = unsafe.getAndAddInt(demo0,ageOffset,100);
            System.out.println("KEVIN-5-getAndAddInt>>"+age+","+demo0.getAge());

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

执行结果

th-1>>11
th-1-getInt>>11
th-2>>11,false
th-3>>100,true
th-4-getAndSetInt>>100,99
th-5-getAndAddInt>>99,199

Unsafe操作static和violate

static

/**
 * Unsafe静态成员操作
 */
public class UnsafeDemo4 {

    public static int state = 0;

    public static void main(String[] args) {
        UnsafeDemo4 demo0 = new UnsafeDemo4();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            //获取静态字段的便宜地址
            long stateOffset = unsafe.staticFieldOffset(UnsafeDemo4.class.getDeclaredField("state"));

            //获取静态字段的值
            int state1 = unsafe.getInt(demo0,stateOffset);
            System.out.println("hodor-1-getInt1>"+state1);

            //获取静态字段的值
            state1 = unsafe.getInt(UnsafeDemo4.class,stateOffset);
            System.out.println("hodor-1-getInt2>"+state1);
            //获取并且增加静态字段的值
            int state2 = unsafe.getAndAddInt(UnsafeDemo4.class,stateOffset,11);
            System.out.println("hodor-2-getAndAddInt>"+state2);
            //再次获取静态字段的值
            int state3 = unsafe.getInt(UnsafeDemo4.class,stateOffset);
            System.out.println("hodor-3-getInt>"+state3);
            //CAS操作
            boolean result = unsafe.compareAndSwapInt(UnsafeDemo4.class,stateOffset,11,12);
            System.out.println("Kevin-4-getInt>"+UnsafeDemo4.state+","+result);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

执行结果,获取偏移地址用staticFieldOffset,get和set操作传入class

hodor-1-getInt1>-316241818
hodor-1-getInt2>0
hodor-2-getAndAddInt>0
hodor-3-getInt>11
Kevin-4-getInt>12,true

violate

**
 * volatile操作
 */
public class UnsafeDemo6 {

    private volatile  int age;

    public int getAge() {
        return age;
    }

    public static void main(String[] args) {
        UnsafeDemo6 demo0 = new UnsafeDemo6();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            //获取age属性的内存偏移地址
            long ageOffset = unsafe.objectFieldOffset(UnsafeDemo1.class.getDeclaredField("age"));

            //设置volatile age的值为11
            unsafe.putIntVolatile(demo0,ageOffset,11);
            System.out.println("KEVIN-1-putIntVolatile>>"+demo0.getAge());

            //获取volatile age的值
            int age = unsafe.getIntVolatile(demo0,ageOffset);
            System.out.println("KEVIN-1-getIntVolatile>>"+age);

            //验证CAS方法
            boolean result = unsafe.compareAndSwapInt(demo0,ageOffset,10,50);
            System.out.println("KEVIN-2-compareAndSwapInt>>"+demo0.getAge()+","+result);
            //验证CAS方法
            result = unsafe.compareAndSwapInt(demo0,ageOffset,11,100);
            System.out.println("KEVIN-3-compareAndSwapInt>>"+demo0.getAge()+","+result);

            //获取并设置
            age = unsafe.getAndSetInt(demo0,ageOffset,99);
            System.out.println("KEVIN-4-getAndSetInt>>"+age+","+demo0.getAge());

            //获取并增加
            age = unsafe.getAndAddInt(demo0,ageOffset,100);
            System.out.println("KEVIN-5-getAndAddInt>>"+age+","+demo0.getAge());

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

执行结果,获取和设置使用getIntVolatile和putIntVolatile

KEVIN-1-putIntVolatile>>11
KEVIN-1-getIntVolatile>>11
KEVIN-2-compareAndSwapInt>>11,false
KEVIN-3-compareAndSwapInt>>100,true
KEVIN-4-getAndSetInt>>100,99
KEVIN-5-getAndAddInt>>99,199

getAndAddInt方法,设置失败就会不断循环,即自旋,保证方法一定会执行成功

public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));

    return var5;
}

这时候再看AtomicLong源码,直接获取了Unsafe类,完全依赖于Unsafe类

// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

得到value字段的偏移地址

/**
 * Returns whether underlying JVM supports lockless CompareAndSet
 * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
 */
private static native boolean VMSupportsCS8();

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicLong.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile long value;

lazySet方法

/**
 * Eventually sets to the given value.
 *
 * @param newValue the new value
 * @since 1.6
 */
public final void lazySet(long newValue) {
    unsafe.putOrderedLong(this, valueOffset, newValue);
}

Unsafe操作Array类

把数组作为整体字段考虑

public class UnsafeDemo2 {

    private long[] salary = {0,1,2,3,4};

    public long[] getSalary() {
        return salary;
    }

    public static void main(String[] args) {
        UnsafeDemo2 demo0 = new UnsafeDemo2();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            //获取大小和容量
            int offset = unsafe.arrayBaseOffset(long[].class);
            //每个元素占用大小
            int scale = unsafe.arrayIndexScale(long[].class);

            System.out.println("hodor-1-offset>>"+offset);
            System.out.println("hodor-2-scale>>"+scale);

            //获取salary字段偏移地址
            long salaryOffset = unsafe.objectFieldOffset(UnsafeDemo2.class.getDeclaredField("salary"));
            //设置数组
            long[] local = new long[]{3,4,5,6};
            unsafe.putObject(demo0,salaryOffset,local);
            System.out.println("hodor-3-put>>"+ Arrays.toString(demo0.getSalary()));
            //CAS成功
            boolean result = unsafe.compareAndSwapObject(demo0,salaryOffset,local,new long[]{9,8,7,6});
            System.out.println("hodor-4-cas>>"+ Arrays.toString(demo0.getSalary())+","+result);
            //CAS失败,引用不同
            result = unsafe.compareAndSwapObject(demo0,salaryOffset,new long[]{9,8,7,6},new long[]{3,3,3,3});
            System.out.println("hodor-4-cas>>"+ Arrays.toString(demo0.getSalary())+","+result);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

输出结果

hodor-1-offset>>16
hodor-2-scale>>8
hodor-3-put>>[3, 4, 5, 6]
hodor-4-cas>>[9, 8, 7, 6],true
hodor-4-cas>>[9, 8, 7, 6],false

数组内元素操作

/**
 * 数组内元素的设置
 */
public class UnsafeDemo3 {

    private long[] salary = {0,1,2,3,4};

    public long[] getSalary() {
        return salary;
    }

    public static void main(String[] args) {
        UnsafeDemo3 demo0 = new UnsafeDemo3();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            int offset = unsafe.arrayBaseOffset(long[].class);
            int scale = unsafe.arrayIndexScale(long[].class);

            System.out.println("hodor-1-offset>>"+offset);
            System.out.println("hodor-2-scale>>"+scale);
            //获取salary字段偏移地址
            long salaryOffset = unsafe.objectFieldOffset(UnsafeDemo3.class.getDeclaredField("salary"));
            //设置数组中第1个元素的值
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*0,9);
            System.out.println("hodor-3-put0>>"+ Arrays.toString(demo0.getSalary()));
            //设置数组中第2个元素的值
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*1,9);
            System.out.println("hodor-3-put1>>"+ Arrays.toString(demo0.getSalary()));
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*2,9);
            System.out.println("hodor-3-put2>>"+ Arrays.toString(demo0.getSalary()));
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*3,9);
            System.out.println("hodor-3-put3>>"+ Arrays.toString(demo0.getSalary()));
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*4,9);
            System.out.println("hodor-3-put4>>"+ Arrays.toString(demo0.getSalary()));
            //设置第6个元素,不会自动扩容
            unsafe.putLong(demo0.getSalary(),(long)offset+scale*5,99);
            System.out.println("hodor-3-put5>>"+ Arrays.toString(demo0.getSalary()));

            //操作局部数组变量
            long[] array = new long[]{1,1,1,1};
            unsafe.putLong(array,(long)offset+scale*0,9);
            System.out.println("hodor-4-put0>>"+ Arrays.toString(array));
            unsafe.putLong(array,(long)offset+scale*1,9);
            System.out.println("hodor-4-put1>>"+ Arrays.toString(array));
            unsafe.putLong(array,(long)offset+scale*2,9);
            System.out.println("hodor-4-put2>>"+ Arrays.toString(array));

            //对数组的某个元素采用cas方法
            boolean result = unsafe.compareAndSwapLong(array,(long)offset+scale*3,9,10);
            System.out.println("hodor-5-cas1>>"+ Arrays.toString(array)+","+result);
            result = unsafe.compareAndSwapLong(array,(long)offset+scale*3,1,10);
            System.out.println("hodor-5-cas2>>"+ Arrays.toString(array)+","+result);

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

执行结果

hodor-1-offset>>16
hodor-2-scale>>8
hodor-3-put0>>[9, 1, 2, 3, 4]
hodor-3-put1>>[9, 9, 2, 3, 4]
hodor-3-put2>>[9, 9, 9, 3, 4]
hodor-3-put3>>[9, 9, 9, 9, 4]
hodor-3-put4>>[9, 9, 9, 9, 9]
hodor-3-put5>>[9, 9, 9, 9, 9]
hodor-4-put0>>[9, 1, 1, 1]
hodor-4-put1>>[9, 9, 1, 1]
hodor-4-put2>>[9, 9, 9, 1]
hodor-5-cas1>>[9, 9, 9, 1],false
hodor-5-cas2>>[9, 9, 9, 10],true

内存操作

1. public native long allocateMemory(long bytes); 分配内存
2. public native long reallocateMemory(long address, long bytes); 重新分配内存
3. public native void setMemory(Object o, long offset, long bytes, byte value);
4. public void setMemory(long address, long bytes, byte value) 初始化内存
5. public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset,
long bytes);
6. public void copyMemory(long srcAddress, long destAddress, long bytes) 复制内存
7. public native void freeMemory(long address);
public class UnsafeDemo5 {

    public static int state = 0;

    public static void main(String[] args) {
        UnsafeDemo5 demo0 = new UnsafeDemo5();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            //获取单个内存地址大小
            int addressSize = unsafe.addressSize();
            System.out.println("hodor-1-addressSize>"+addressSize);
            //获取内存页大小
            int pageSize = unsafe.pageSize();
            System.out.println("hodor-2-pageSize>"+pageSize);

            //分配8字节内存,并返回内存首地址
            long address = unsafe.allocateMemory(8L);
            System.out.println("hodor-3-allocateMemory>"+address);

            //初始化内存
            unsafe.setMemory(address,8L, (byte)1);

            //设置内存地址的值,为10 占用4字节
            unsafe.putInt(address,10);
            System.out.println("hodor-4-getInt:" + unsafe.getInt(address));

            //设置内存地址+4的值,为10
            unsafe.putInt(address+4,12);
            System.out.println("hodor-5-getInt:" + unsafe.getInt(address+4));

            //设置内存地址+8的值,为13
            unsafe.putInt(address+4+4,13);
            System.out.println("hodor-6-getInt:" + unsafe.getInt(address+4+4));

            //覆盖起始地址值为8L
            unsafe.putLong(address,81L);
            System.out.println("hodor-7-getLong:" + unsafe.getLong(address));

            System.out.println("hodor-8-getInt:"  + unsafe.getInt(address+4+4));

            //再分配一块内存
            long newAddress = unsafe.allocateMemory(8l);
            //copy内存
            unsafe.copyMemory(address,newAddress,8L);
            System.out.println("hodor-9-getLong:" + unsafe.getLong(newAddress));

            //释放内存 后面获取的值是不确定的
            unsafe.freeMemory(address);
            System.out.println("hodor-10-getLong:" + unsafe.getLong(address));

            //hodor: 重复释放会导致JRE异常
            //unsafe.freeMemory(address);


        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

输出结果

hodor-1-addressSize>8
hodor-2-pageSize>4096
hodor-3-allocateMemory>55698736
hodor-4-getInt:10
hodor-5-getInt:12
hodor-6-getInt:13
hodor-7-getLong:81
hodor-8-getInt:13
hodor-9-getLong:81
hodor-10-getLong:81

线程调度

1. public native void park(boolean isAbsolute, long time); 挂起线程
2. public native void unpark(Object thread); 唤醒线程
3. 需要注意线程的interrupt方法同样能唤醒线程,但是不报错
4. java.util.concurrent.locks.LockSupport使用unsafe实现
/**
 * park 和 unpuark
 */
public class UnsafeDemo7 {

    private volatile  int age;

    public int getAge() {
        return age;
    }

    public static void main(String[] args) {
        UnsafeDemo7 demo0 = new UnsafeDemo7();
        try {
            //通过反射获取Unsafe的静态成员变量theUnsafe
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            //设置此字段为可存取
            field.setAccessible(true);
            //获取变量的值,强转为Unsafe类对象
            Unsafe unsafe = (Unsafe) field.get(null);

            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"-start" );
                    //hodor:非绝对时间为纳秒 等待两秒
                    unsafe.park(false,2000*1000000);
                    //hodor:绝对时间为毫秒
//                    unsafe.park(true,System.currentTimeMillis()+2000);
                    //hodor:配合interrupte查看效果
//                    if(Thread.currentThread().isInterrupted()){
//                        System.out.println(Thread.currentThread().getName()+"-interrupte" );
//                    }
                    System.out.println(Thread.currentThread().getName()+"-end" );
                }
            });

            t.start();

            System.out.println(Thread.currentThread().getName()+"-run" );

            //hodor:打开关闭查看效果
            //unsafe.unpark(t);
            //hodor:打开关闭查看效果
//            t.interrupt();


        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

输出结果,等待两秒后输出Thread-0-end

main-run
Thread-0-start
Thread-0-end

打开unpark瞬间执行结束

interrupt打上中断标记,sleep join wait方法会报异常,使用interrupt没有抛出异常,打开interrupt和if判断,执行结果如下

main-run
Thread-0-start
Thread-0-interrupte
Thread-0-end

Unsafe的内存屏障和类加载

内存屏障(保证代码执行的顺序)

1. public native void loadFence(); 保证在这个屏障之前的所有读操作都已经完成
2. public native void storeFence(); 保证在这个屏障之前的所有写操作都已经完成
3. public native void fullFence(); 保证在这个屏障之前的所有读写操作都已经完成

类加载

1. public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader,
ProtectionDomain protectionDomain); 方法定义一个类,用于动态地创建类。
2. public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[]
cpPatches);用于动态的创建一个匿名内部类。
3. public native Object allocateInstance(Class<?> cls) throws InstantiationException;方法用于创建一
个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。
4. public native boolean shouldBeInitialized(Class<?> c);方法用于判断是否需要初始化一个类。
5. public native void ensureClassInitialized(Class<?> c);方法用于保证已经初始化过一个类。

反射newInstance调用了构造方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值