Unsafe类对象在cas中是非常重要的,不管是在原子整数
AtomicInteger
,AtomicLong
,原子引用AtomicReference,包括AtomicStampedReference和AtomicMarkableReference,原子数组AtomicIntegerArray,AtomicLongArray,还有原子更新器AtomicIntegerFieldUpdater等,都是通过unsafe对象中的cas方法来实现的。
1. 创建Unsafe对象并使用
Unsafe对象不能通过new 来创建,只能通过反射的方法,代码如下所示:
class Test31 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
System.out.println(unsafe);
Student student = new Student();
long idOffset = unsafe.objectFieldOffset(Student.class.getDeclaredField("id"));
long nameOffset = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));
System.out.println(unsafe.compareAndSwapInt(student, idOffset, 0, 1));
System.out.println(unsafe.compareAndSwapObject(student, nameOffset, null, "张三"));
System.out.println(student);
}
static class Student {
private int id;
private String name;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
}
说明: Unsafe类中有个私有的变量叫做unsafe,可以通过反射获取这个unsafe对象,在3-5行代码通过反射实现了对unsafe对象的获取,在后续使用了unsafe中的cas方法对变量进行线程安全的修改,cas方法中有传递四个参数,这里称之为var1,var2,var3,var4,var1位要更改属性的对象,var2是偏移量,可以通过unsafe对象获取,var3为当前线程读取到该对象属性的旧值,这个旧值需要去和主存中的值进行比较,如果相同,更新为var4的值。
其中Unsafe类的私有变量如下所示
运行结果:
2. 用unsafe来实现AtomicInteger
下面举了3个例子来来对自己实现的AtomicInteger进行验证,都以10000元,启动1000个线程,每个线程取10元为例。
- 示例1 (原始方式)
// 下面的方式是线程不安全的,用了Integer来修饰共享变量 class AccountTest { private Integer balance; public AccountTest(Integer balance) { this.balance = balance; } public void withdraw(Integer amount) { this.balance = balance - amount; } public Integer getBalance() { return balance; } public static void main(String[] args) { AccountTest account = new AccountTest(10000); List<Thread> threads = new ArrayList<>(); for (int i=0;i<1000;i++) { threads.add(new Thread(() -> { account.withdraw(10); })); } threads.forEach(Thread::start); threads.forEach(thread -> { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(account.getBalance()); } }
运行结果:
- 示例2(
AtomicInteger
方式)class AccountTest1 { private AtomicInteger balance; public AccountTest1(Integer balance) { this.balance = new AtomicInteger(balance); } public void withdraw(Integer amount) { balance.updateAndGet((x) -> x - amount); } public Integer getBalance() { return balance.get(); } public static void main(String[] args) { AccountTest1 account = new AccountTest1(10000); List<Thread> threads = new ArrayList<>(); for (int i=0;i<1000;i++) { threads.add(new Thread(() -> { account.withdraw(10); })); } threads.forEach(Thread::start); threads.forEach(thread -> { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(account.getBalance()); } }
运行结果:
3.示例3 (自己实现)
实现类
class MyAtomicInteger { private static Unsafe UNSAFE; private volatile int value; private static long valueOffset; static { UNSAFE = UnsafeAccessor.getUnsafe(); try { valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value")); } catch (NoSuchFieldException e) { e.printStackTrace(); } } public MyAtomicInteger(int value) { this.value = value; } public int get() { return value; } public void updateAndGet(int amount) { while (true) { int prev = this.value; int next = prev - amount; if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) { break; } } } }
测试类:
class AccountTest2 { private MyAtomicInteger balance; public AccountTest2(Integer balance) { this.balance = new MyAtomicInteger(balance); } public void withdraw(Integer amount) { balance.updateAndGet(amount); } public Integer getBalance() { return balance.get(); } public static void main(String[] args) { AccountTest2 account = new AccountTest2(10000); List<Thread> threads = new ArrayList<>(); for (int i=0;i<1000;i++) { threads.add(new Thread(() -> { account.withdraw(10); })); } threads.forEach(Thread::start); threads.forEach(thread -> { try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(account.getBalance()); } }
- 示例2(
总结(重要)
这里对整个cas无锁并发做个总结,拿
AtomicInteger
,AtomicReference
,AtomicIntegerArray
,AtomicIntegerFieldUpdater
举例子,他们都包含其他compareAndSet方法,这个方法的底层都是调用unsafe对象的compareAndSwap*方法,具体如下所示。
以上这些方法都会调到Unsafe里面的下面三个方法,第一个参数是原子整数或者引用对象,第二个参数为value的偏移量,根据这个原子对象和偏移量可以得到主存中的最新值,然后主存和当前线程的var4比,如果相同,则更改变量为var5,如果不同则重试!
文章评论