一、原子类
1.1 基本原子类
- AtomicBoolean
- AtomicInteger
- AtomicLong
1.1.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)
1.1.2 基本使用
public class AtomicIntegerDemo {public static final int SIEZ_ = 50;public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInteger = new AtomicInteger();CountDownLatch countDownLatch = new CountDownLatch(SIEZ_);for (int i = 1; i <=SIEZ_; i++) {new Thread(() -> {try {for (int j = 1 ;j <=1000; j++) {atomicInteger.incrementAndGet();}}catch (Exception e){e.printStackTrace();}finally {countDownLatch.countDown();}},String.valueOf(i)).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName()+"\t"+"---result : "+atomicInteger.get());}
}
1.2 数组类型原子类
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
1.2.1 基本使用
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();System.out.println();System.out.println();int tmpInt = 0;tmpInt = atomicIntegerArray.getAndSet(0,1122);System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));atomicIntegerArray.getAndIncrement(1);atomicIntegerArray.getAndIncrement(1);tmpInt = atomicIntegerArray.getAndIncrement(1);System.out.println(tmpInt+"\t"+atomicIntegerArray.get(1));}
}
1.3 引用类型原子类
- AtomicReference
1.3.1 AtomicReference基本使用
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());}
}
AtomicReference实现自旋锁
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
AtomicStampedReference是携带版本号的引用类型原子类,可以解决ABA问题。解决修改过几次
1.3.2 AtomicStampedReference基本使用
static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);public static void main(String[] args) {new Thread(() -> {int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"---默认版本号: "+stamp);//让后面的t4获得和t3一样的版本号,都是1,好比较try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);System.out.println(Thread.currentThread().getName()+"\t"+"---1次版本号: "+atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t"+"---2次版本号: "+atomicStampedReference.getStamp());},"t3").start();new Thread(() -> {int stamp = atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t"+"---默认版本号: "+stamp);//上前面的t3完成ABA问题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.getStamp()+"\t"+atomicStampedReference.getReference());},"t4").start();}
- AtomicMarkableReference
原子更新带有标记位的引用类型对象 。只关心是否被修改过
1.3.3 AtomicMarkableReference基本使用
public class ABADemo{static AtomicInteger atomicInteger = new AtomicInteger(100);static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);static AtomicMarkableReference<Integer> markableReference = new AtomicMarkableReference<>(100,false);public static void main(String[] args){new Thread(() -> {atomicInteger.compareAndSet(100,101);atomicInteger.compareAndSet(101,100);System.out.println(Thread.currentThread().getName()+"\t"+"update ok");},"t1").start();new Thread(() -> {//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }atomicInteger.compareAndSet(100,2020);},"t2").start();//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(atomicInteger.get());System.out.println();System.out.println();System.out.println();System.out.println("============以下是ABA问题的解决,让我们知道引用变量中途被更改了几次=========================");new Thread(() -> {System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+stampedReference.getStamp());//故意暂停200毫秒,让后面的t4线程拿到和t3一样的版本号try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t 2次版本号"+stampedReference.getStamp());stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);System.out.println(Thread.currentThread().getName()+"\t 3次版本号"+stampedReference.getStamp());},"t3").start();new Thread(() -> {int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName()+"\t =======1次版本号"+stamp);//暂停2秒钟,让t3先完成ABA操作了,看看自己还能否修改try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }boolean b = stampedReference.compareAndSet(100, 2020, stamp, stamp + 1);System.out.println(Thread.currentThread().getName()+"\t=======2次版本号"+stampedReference.getStamp()+"\t"+stampedReference.getReference());},"t4").start();System.out.println();System.out.println();System.out.println();System.out.println("============AtomicMarkableReference不关心引用变量更改过几次,只关心是否更改过======================");new Thread(() -> {boolean marked = markableReference.isMarked();System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }markableReference.compareAndSet(100,101,marked,!marked);System.out.println(Thread.currentThread().getName()+"\t 2次版本号"+markableReference.isMarked());markableReference.compareAndSet(101,100,markableReference.isMarked(),!markableReference.isMarked());System.out.println(Thread.currentThread().getName()+"\t 3次版本号"+markableReference.isMarked());},"t5").start();new Thread(() -> {boolean marked = markableReference.isMarked();System.out.println(Thread.currentThread().getName()+"\t 1次版本号"+marked);//暂停几秒钟线程try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }markableReference.compareAndSet(100,2020,marked,!marked);System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference()+"\t"+markableReference.isMarked());},"t6").start();}
}
1.4 对象的属性修改原子类
- AtomicIntegerFieldUpdater
原子更新对象中int类型字段的值
- AtomicLongFieldUpdater
原子更新对象中Long类型字段的值
- AtomicReferenceFieldUpdater
原子更新引用类型字段的值
1.4.1 作用
以一种线程安全的方式操作非线程安全对象内的某些字段
1.4.2 使用要求
- 更新的对象属性必须使用 public volatile 修饰符。
- 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
1.4.3 基本使用
- AtomicIntegerFieldUpdaterDemo
class BankAccount
{String bankName = "boc";//以一种线程安全的方式操作非线程安全对象内的某些字段//1 更新的对象属性必须使用 public volatile 修饰符。public volatile int money = 0;//2 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须// 使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。AtomicIntegerFieldUpdater FieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");public void transfer(BankAccount bankAccount){FieldUpdater.incrementAndGet(bankAccount);}
}/*** 以一种线程安全的方式操作非线程安全对象的某些字段。* 需求:* 1000个人同时向一个账号转账一元钱,那么累计应该增加1000元,* 除了synchronized和CAS,还可以使用AtomicIntegerFieldUpdater来实现。*/
public class AtomicIntegerFieldUpdaterDemo
{public static void main(String[] args) throws InterruptedException{BankAccount bankAccount = new BankAccount();CountDownLatch countDownLatch = new CountDownLatch(1000);for (int i = 1; i <=1000; i++) {new Thread(() -> {bankAccount.transfer(bankAccount);countDownLatch.countDown();},String.valueOf(i)).start();}countDownLatch.await();System.out.println(Thread.currentThread().getName()+"\t"+"---bankAccount: "+bankAccount.money);}
}
- AtomicReferenceFieldUpdater
class MyVar {public volatile Boolean isInit = Boolean.FALSE;AtomicReferenceFieldUpdater<MyVar,Boolean> FieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");public void init(MyVar myVar) {if(FieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)){System.out.println(Thread.currentThread().getName()+"\t"+"---start init");try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(Thread.currentThread().getName()+"\t"+"---end 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();}}
}