变量与线程安全
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解决了原子性和可见性的问题,但是会造成频繁的上下文切换,造成性能开销
- JDK提供的非阻塞原子操作,通过硬件保证了比较、更新操作的原子性
- JDK的Unsafe类提供了一系列的compareAndSwap*方法来支持CAS操作
本质上是乐观锁,下图是原理
CAS-ABA问题
- 如果程序按照1~5的顺序执行,依然是成功的,然而线程1修改时x的值时其实已经从x= A =>B =>A。
- 由于变量的值产生了环形转换,从A变为B又变回了A。如果不存在环形转换也就不存在ABA问题。
使用版本号可以解决唤醒转换的问题
- 给变量分配时间戳(版本)、版本来解决ABA问题
- 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类安全限定
典型的单例方法
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调用了构造方法