注意:就第三个方法来说,如果是写a++ 那么其实它是分成三个部分的。a先赋值,a加1,a再重新赋值。 原子类线程安全代码如下;
package atomic; import java.util.concurrent.atomic.AtomicInteger; /** * 演示AtomicInteger的基本用法,对比非原子类的线程安全问题 * 使用了原子类之后,不需要加锁,也可以保证线程安全。 */ public class AtomicIntegerDemo1 implements Runnable{ private static final AtomicInteger atomicInteger = new AtomicInteger(); public void incrementAtomic(){ //自增 atomicInteger.getAndIncrement(); } //volatile 取消了cpu缓存 private static volatile int basicCount = 0; public void incrementBasic(){ basicCount++; } @Override public void run() { for (int i = 0; i < 10000; i++) { incrementAtomic(); incrementBasic(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo1 atomicIntegerDemo1 = new AtomicIntegerDemo1(); Thread t1 = new Thread(atomicIntegerDemo1); Thread t2 = new Thread(atomicIntegerDemo1); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("原子类的结果是:" + atomicInteger.get()); System.out.println(basicCount); } }结果如下:
package atomic; import jdk.nashorn.internal.ir.CallNode; import java.util.concurrent.atomic.AtomicIntegerArray; /** * 演示原子数组的使用方法 */ public class AtomicArrayDemo { public static void main(String[] args) { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); // 新建两个Runnable对象 Incrementer incrementer = new Incrementer(atomicIntegerArray); Decrementer decrementer = new Decrementer(atomicIntegerArray); Thread[] threadsIncrementer = new Thread[200]; Thread[] threadsDecrementer = new Thread[200]; for (int i = 0; i < 100; i++) { threadsDecrementer[i] = new Thread(decrementer); threadsIncrementer[i] = new Thread(incrementer); threadsDecrementer[i].start(); threadsIncrementer[i].start(); } // 目的是为了精确判断子线程执行完毕 for (int i = 0; i < 100; i++) { try { threadsDecrementer[i].join(); threadsIncrementer[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < atomicIntegerArray.length(); i++) { if(atomicIntegerArray.get(i) != 0){ System.out.println("发现错误" + i); } } System.out.println("结果正确,运行结束"); } } class Decrementer implements Runnable{ private AtomicIntegerArray array; public Decrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { for (int i = 0; i < array.length(); i++) { // 递减 array.getAndDecrement(i); } } } class Incrementer implements Runnable{ private AtomicIntegerArray array; public Incrementer(AtomicIntegerArray array) { this.array = array; } @Override public void run() { // 递增 for (int i = 0; i < array.length(); i++) { // 获取i索引的值,并递增 array.getAndIncrement(i); } } }输出结果是结果正确。 通过比较可以看到AtomicIntegerArray 具备原子性的 100个线程分别进行递增和递减复杂情况下,都不会对他产生干扰。 用原子类来实现自旋锁的例子中: 创建AtomicReference时,传入的泛型是Thread说明里面只能加入Thread 其compareAndSet方法,如果里面是null,就将其设置为current。 如果其不为null,就返回false。 所以,第一次可以设置以后,即加锁以后,其它线程就不可以进行设置了。只有它进行释放以后,即解锁,才可以进行再次加锁,这就是自旋锁的原理。
package atomic; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; public class AtomicIntegerFieldUpdaterDemo implements Runnable{ private static Candidate tom; private static Candidate peter; private static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score"); @Override public void run() { for (int i = 0; i < 10000; i++) { peter.score++; scoreUpdater.getAndIncrement(tom); } } public static class Candidate{ volatile int score; } public static void main(String[] args) { tom = new Candidate(); peter = new Candidate(); AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); // 等待线程运行结束 try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(tom.score); System.out.println(peter.score); } }结果如下 很明显用了AtomicIntegerFieldUpdater以后,可以让它具备原子性。AtomicIntegerFieldUpdater 可以用在并发情况不多,偶尔需要并发的时候,如果一直都需要并发的话,还是直接就设置成为AtomicInteger即可。 在需要原子性的时候,进行升级就使用AtomicIntegerFieldUpdater 使用AtomicIntegerFieldUpdater要注意的事项, 第一 修饰变量要可见不能使用private修饰 第二 不能用static来修饰变量 下面是以AtomicLong原子类来实现原子性
package atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; /** * 描述: 演示高并发场景下,LongAdder比AtomicLong性能好 */ public class AtomicLongDemo { public static void main(String[] args) { AtomicLong counter = new AtomicLong(0); ExecutorService service = Executors.newFixedThreadPool(16); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { service.submit(new Task(counter)); } service.shutdown(); while(!service.isTerminated()){ } long endTime = System.currentTimeMillis(); System.out.println(counter.get()); System.out.println(endTime - startTime); } private static class Task implements Runnable{ private AtomicLong counter; @Override public void run() { for (int i = 0; i < 10000; i++) { counter.incrementAndGet(); } } public Task(AtomicLong counter) { this.counter = counter; } } }耗时时间为700+毫秒 下面是以LongAdder来实现原子性
package atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAdder; /** * 描述: 演示高并发场景下,LongAdder比AtomicLong性能好 */ public class LongAdderDemo { public static void main(String[] args) { LongAdder counter = new LongAdder(); ExecutorService service = Executors.newFixedThreadPool(16); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { service.submit(new Task(counter)); } service.shutdown(); while(!service.isTerminated()){ } long endTime = System.currentTimeMillis(); System.out.println(counter.sum()); System.out.println(endTime - startTime); } private static class Task implements Runnable{ private LongAdder counter; @Override public void run() { for (int i = 0; i < 10000; i++) { counter.increment(); } } public Task(LongAdder counter) { this.counter = counter; } } }耗时时间为80+,所以,它们之间的效率其实相差也挺大的。 原因如下:AtomoicLong 在cpu上有线程1和线程2,当我修改了thread-1的ctr,线程2是看不到的,它们必须通过下面这个主内存,线程1先将ctr flush到主内存中,而线程2再把它refresh到才可以。注意,当cpu线程1更改了这个值以后,其他线程都会去重新refresh。 LongAdder 是线程1中的ctr 和线程2中的ctr是独立的。
package atomic; import java.util.concurrent.atomic.LongAccumulator; /** * 描述: 演示LongAccumulator的用法 */ public class LongAccumulatorDemo { public static void main(String[] args) { // identity代表的是初始值 LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0); // 后面代表的是要加上的值 accumulator.accumulate(1); accumulator.accumulate(2); System.out.println(accumulator.getThenReset()); } }结论为3。
package atomic; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.LongAccumulator; import java.util.stream.IntStream; /** * 描述: 演示LongAccumulator的用法 */ public class LongAccumulatorDemo { public static void main(String[] args) { // identity代表的是初始值 LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0); ExecutorService executorService = Executors.newFixedThreadPool(8); // 用流进行提交任务 IntStream.range(1,10).forEach(i->executorService.submit(()->accumulator.accumulate(i))); executorService.shutdown(); while(!executorService.isTerminated()){} // 答案为45 System.out.println(accumulator.getThenReset()); } }结论为45 用了它,就是比较灵活。 比如说,进行简单更改,那么答案就是9了。 使用场景:适合于大数据计算,并发的场景, 而且顺序不同,计算结果要相同。 比如说,加是可以的,乘也是可以的。 但是,减和除就不行了,因为并发执行的话,不知道那个线程先执行,而减法和除法对顺序有要求。
