1. 基本类型原子类
AtomicInteger
AtomicBoolean
AtomicLong
1.常用API简介
public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值,并设置新的值 public final int getAndIncrement()//获取当前的值,并自增 public final int getAndDecrement() //获取当前的值,并自减 public final int getAndAdd(int delta) //获取当前的值,并加上预期的值 boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
2.Thread.sleep→CountDownLatch
class MyNumber { AtomicInteger atomicInteger = new AtomicInteger(); public void addPlusPlus() { atomicInteger.getAndIncrement(); } } public class AtomicIntegerDemo { public static final int SIZE = 50; public static void main(String[] args) throws InterruptedException { MyNumber myNumber = new MyNumber(); CountDownLatch countDownLatch = new CountDownLatch(SIZE); for (int i = 1; i <=SIZE; i++) { new Thread(() -> { try { for (int j = 1; j <=1000; j++) { myNumber.addPlusPlus(); } } finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } //等待上面50个线程全部计算完成后,再去获得最终值 //暂停几秒钟线程 //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get()); } }
2. 数组类型原子类
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
public class AtomicIntegerArrayDemo { public static void main(String[] args) { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]); //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5); //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5}); for (int i = 0; i <atomicIntegerArray.length(); i++) { System.out.println(atomicIntegerArray.get(i)); } System.out.println(); int tmpInt = 0; tmpInt = atomicIntegerArray.getAndSet(0,1122); //下标为0的赋值为1122 System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));//获取下标0的值 tmpInt = atomicIntegerArray.getAndIncrement(0); //下标0的值自增1 System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0)); //获取下标0的值 /** 最后输出结果: 0 0 0 0 0 0 1122 1122 1123 */ } }
3.引用类型原子类
AtomicReference
@Getter @ToString @AllArgsConstructor class User { String userName; int age; } public class AtomicReferenceDemo { public static void main(String[] args) { User z3 = new User("z3",24); User li4 = new User("li4",26); AtomicReference<User> atomicReferenceUser = new AtomicReference<>(); atomicReferenceUser.set(z3); System.out.println(atomicReferenceUser.compareAndSet(z3,li4)+"\t"+atomicReferenceUser.get().toString()); System.out.println(atomicReferenceUser.compareAndSet(z3,li4)+"\t"+atomicReferenceUser.get().toString()); } }
自旋锁SpinLockDemo
/** * 要求:实现一个自旋锁 * 自旋锁好处:循环比较获取没有类似wait的阻塞。 * * 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B随后进来后发现 * 当前有线程持有锁,不是null,所以只能通过自旋等待,直到A释放锁后B随后抢到。 */ public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void myLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName()+"\t come in"); while(!atomicReference.compareAndSet(null,thread)) { } } public void myUnLock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread,null); System.out.println(Thread.currentThread().getName()+"\t myUnLock over"); } public static void main(String[] args) { SpinLockDemo spinLockDemo = new SpinLockDemo(); new Thread(() -> { spinLockDemo.myLock(); //暂停一会儿线程 try { TimeUnit.SECONDS.sleep( 5 ); } catch (InterruptedException e) { e.printStackTrace(); } spinLockDemo.myUnLock(); },"A").start(); //暂停一会儿线程,保证A线程先于B线程启动并完成 try { TimeUnit.SECONDS.sleep( 1 ); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { spinLockDemo.myLock(); spinLockDemo.myUnLock(); },"B").start(); } }
AtomicStampedReference
携带版本号的引用类型原子类,可以解决ABA问题
解决修改过几次
状态戳原子引用
ABADemo
public class ABADemo { static AtomicInteger atomicInteger = new AtomicInteger(100); static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1); public static void main(String[] args) { abaProblem(); abaResolve(); } public static void abaResolve() { new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println("t3 ----第1次stamp "+stamp); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100,101,stamp,stamp+1); System.out.println("t3 ----第2次stamp "+atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); System.out.println("t3 ----第3次stamp "+atomicStampedReference.getStamp()); },"t3").start(); new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println("t4 ----第1次stamp "+stamp); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = atomicStampedReference.compareAndSet(100, 20210308, stamp, stamp + 1); System.out.println(Thread.currentThread().getName()+"\t"+result+"\t"+atomicStampedReference.getReference()); },"t4").start(); } public static void abaProblem() { new Thread(() -> { atomicInteger.compareAndSet(100,101); atomicInteger.compareAndSet(101,100); },"t1").start(); try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { atomicInteger.compareAndSet(100,20210308); System.out.println(atomicInteger.get()); },"t2").start(); } }
AtomicMarkableReference
原子更新带有标记位的引用类型对象
解决是否修改过 (将状态戳简化为true|false),一次性的
状态戳(true/false)原子引用
public class AtomicMarkableReferenceDemo { static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false); public static void main(String[] args) { new Thread(() -> { boolean marked = markableReference.isMarked(); System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked); //暂停1秒钟线程,等待后面的T2线程和我拿到一样的模式flag标识,都是false try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } markableReference.compareAndSet(100,1000,marked,!marked); },"t1").start(); new Thread(() -> { boolean marked = markableReference.isMarked(); System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } boolean b = markableReference.compareAndSet(100, 2000, marked, !marked); System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CASresult: "+b); System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked()); System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference()); },"t2").start(); } } /** * CAS----Unsafe----do while+ABA---AtomicStampedReference,AtomicMarkableReference * * AtomicStampedReference,version号,+1; * * AtomicMarkableReference,一次,false,true * */
4.对象的属性修改原子类
AtomicIntegerFieldUpdater
原子更新对象中int类型字段的值
AtomicLongFieldUpdater
原子更新对象中Long类型字段的值
AtomicReferenceFieldUpdater
原子更新引用类型字段的值
1.使用目的
以一种线程安全的方式操作非线程安全对象内的某些字段
2.使用要求
更新的对象属性必须使用 public volatile 修饰符。
因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
3.AtomicIntegerFieldUpdaterDemo
//资源类 class BankAccount { String bankName = "CCB"; //更新的对象属性必须使用 public volatile 修饰符。 public volatile int money = 0;//钱数 public void add() //synchronized锁方式 { money++; } //因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须 // 使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。 AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money"); //不加synchronized,保证高性能原子性,局部微创小手术 //参数:对哪个对象操作 public void transMoney(BankAccount bankAccount) { fieldUpdater.getAndIncrement(bankAccount); } } /** * 以一种线程安全的方式操作非线程安全对象的某些字段。 * <p> * 需求: * 10个线程, * 每个线程转账1000, * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现。 */ public class AtomicIntegerFieldUpdaterDemo { public static void main(String[] args) throws InterruptedException { BankAccount bankAccount = new BankAccount(); CountDownLatch countDownLatch = new CountDownLatch(10); for (int i = 1; i <= 10; i++) { new Thread(() -> { try { for (int j = 1; j <= 1000; j++) { //bankAccount.add(); synchronized锁方式 bankAccount.transMoney(bankAccount); } } finally { countDownLatch.countDown(); } }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "\t" + "result: " + bankAccount.money); } }
4.AtomicReferenceFieldUpdater
//资源类 class MyVar { public volatile Boolean isInit = Boolean.FALSE; AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit"); public void init(MyVar myVar) { if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)) { System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds"); //暂停几秒钟线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"----- over init"); }else{ System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。"); } } } /** * 需求: * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作, * 要求只能被初始化一次,只有一个线程操作成功 */ public class AtomicReferenceFieldUpdaterDemo { public static void main(String[] args) { MyVar myVar = new MyVar(); for (int i = 1; i <=5; i++) { new Thread(() -> { myVar.init(myVar); },String.valueOf(i)).start(); } } } /** 输出结果: 2 ----- start init,need 2 seconds 1 ----- 已经有线程在进行初始化工作。。。。。 4 ----- 已经有线程在进行初始化工作。。。。。 3 ----- 已经有线程在进行初始化工作。。。。。 5 ----- 已经有线程在进行初始化工作。。。。。 2 ----- over init 总结:多线程环境下,存在并发情况,实际执行结果并不一定是线程1抢到初始化,每次都是不确定的。使用AtomicReferenceFieldUpdater可以保证有且只有一次初始化执行 */
5.你在哪里用了volatile
AtomicReferenceFieldUpdater(相当有含金量的回答)
5.原子操作增强类原理深度解析
Java8新特性
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
1.模拟点赞计数器,查看性能
LongAdder只能用来计算加法,且从零开始计算
LongAccumulator提供了自定义的函数操作
public class LongAdderAPIDemo { public static void main(String[] args) { LongAdder longAdder = new LongAdder(); longAdder.increment(); //自增1 longAdder.increment();//自增1 longAdder.increment();//自增1 System.out.println(longAdder.sum()); //3 LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() { @Override public long applyAsLong(long left, long right) { return left + right;//可以自定义加减乘除,各种计算 } },0); //可以自定义初始值 longAccumulator.accumulate(1);//1 longAccumulator.accumulate(3);//4 System.out.println(longAccumulator.get()); } }