一些原子类的简单用法
讲解视频参考B站视频讲解链接
package com;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;
import java.util.function.LongBinaryOperator;
/**
* 学习使用Atomic类的一些常见方法
*/
public class AtomicTest {
public static final int SIZE = 50;
public static final int count = 10000;
public static void main(String[] args) throws InterruptedException {
test7();
}
/**
* 案例:热点商品点赞计数器,点赞数使用加加统计,不要求实时精确
*
* 50个线程,每个线程点赞100W次,统计总点赞数
*
* todo:
* 统计比较四种方案的性能
* ----clickBySynchronized cost time: 1696毫秒 50000000
* ----clickByAtomicLong cost time: 1091毫秒 50000000
* ----clickByLongAdder cost time: 155毫秒 50000000
* ----clickByLongAccumulator cost time: 134毫秒 50000000
* 结论:推荐使用LongAdder,该类的性能较高,操作简单
*
*
*/
private static void test7() throws InterruptedException {
ClickNumber clickNumber = new ClickNumber();
long startTime;
long endTime;
CountDownLatch countDownLatch1 = new CountDownLatch(SIZE);
CountDownLatch countDownLatch2 = new CountDownLatch(SIZE);
CountDownLatch countDownLatch3 = new CountDownLatch(SIZE);
CountDownLatch countDownLatch4 = new CountDownLatch(SIZE);
startTime = System.currentTimeMillis();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
try {
for (int j = 0; j < count * 100; j++) {
clickNumber.clickBySynchronized();
}
} finally {
countDownLatch1.countDown();
}
}, "t1").start();
}
countDownLatch1.await();
endTime = System.currentTimeMillis();
System.out.println("----clickBySynchronized cost time: " + "\t"
+ (endTime - startTime) + "毫秒" + "\t" + clickNumber.number);
startTime = System.currentTimeMillis();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
try {
for (int j = 0; j < count * 100; j++) {
clickNumber.clickByAtomicLong();
}
} finally {
countDownLatch2.countDown();
}
}, "t1").start();
}
countDownLatch2.await();
endTime = System.currentTimeMillis();
System.out.println("----clickByAtomicLong cost time: " + "\t"
+ (endTime - startTime) + "毫秒" + "\t" + clickNumber.atomicLong.get());
startTime = System.currentTimeMillis();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
try {
for (int j = 0; j < count * 100; j++) {
clickNumber.clickByLongAdder();
}
} finally {
countDownLatch3.countDown();
}
}, "t1").start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("----clickByLongAdder cost time: " + "\t"
+ (endTime - startTime) + "毫秒" + "\t" + clickNumber.longAdder.sum());
startTime = System.currentTimeMillis();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
try {
for (int j = 0; j < count * 100; j++) {
clickNumber.clickByLongAccumulator();
}
} finally {
countDownLatch4.countDown();
}
}, "t1").start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("----clickByLongAccumulator cost time: " + "\t"
+ (endTime - startTime) + "毫秒" + "\t" + clickNumber.longAccumulator.get());
}
/**
* LongAdder只能从0开始 LongAccumulator可以自定义加减乘除 更加灵活
*/
private static void test6() {
LongAdder adder = new LongAdder();
adder.increment();
adder.increment();
adder.increment();
System.out.println(adder);
LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left +right;
}
}, 0);
accumulator.accumulate(1);
accumulator.accumulate(4);
System.out.println(accumulator.get());
}
/**
* 多线程并发调用一个初始化方法,如果未初始化则调用,反之则不
*/
private static void test5() {
MyVar myVar = new MyVar();
for (int i = 0; i < SIZE; i++) {
new Thread(()->{
myVar.init();
}, String.valueOf(i)).start();
}
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
}
/**
* 以一种线程安全的方式操作非线程安全的对象中的某个字段
* 使用场景:一个账号类BankAccount的实例字段money 如果多个账号给这个对象转钱,
* 那么如果add方法使用synchronized修饰,则锁的是整个对象,势必会造成性能降低,
* 那么使用AtomicReferenceFieldUpdater可以使money具有原子性!!!
* TODO:
* 使用AtomicReferenceFieldUpdater实现
*/
private static void test4() throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
for (int i1 = 0; i1 < 1000; i1++) {
bankAccount.transMoney();
}
} finally {
countDownLatch.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(bankAccount.money);
}
/**
* 测试AtomicMarkableReference的用法 AtomicMarkableReference对象只能修改一次!!!!
*/
private static void test3() {
AtomicMarkableReference<Integer> markableReference = new AtomicMarkableReference<>(100, false);
new Thread(()->{
boolean marked = markableReference.isMarked();
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.compareAndSet(100, 1000, marked, !marked));
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.compareAndSet(1000, 100, !marked, marked));
}, "t1").start();
new Thread(()->{
boolean marked = markableReference.isMarked();
try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.compareAndSet(100, 2000, marked, !marked));
System.out.println(markableReference.getReference());
}, "t2").start();
try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
}
/**
* 使用CountDownLatch解决test1中的问题
*/
private static void test2() throws InterruptedException {
MyNumber myNumber = new MyNumber();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 1; i <= SIZE; i++) {
new Thread(()->{
try {
for (int j = 0; j < 1000; j++) {
myNumber.addPlusPlus();
}
} finally {
countDownLatch.countDown();
}
}, "t1").start();
}
countDownLatch.await();
System.out.println(myNumber.atomicInteger.get());
}
/**
* 虽然没有出现问题,但是具体主线程需要等待多少时间是未知的,在具体的生产环境中并不适用!!!
*/
private static void test1() {
MyNumber myNumber = new MyNumber();
for (int i = 1; i <= SIZE; i++) {
new Thread(()->{
for (int i1 = 0; i1 < 1000; i1++) {
myNumber.addPlusPlus();
}
}, "t1").start();
}
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(myNumber.atomicInteger.get());
}
}
class MyVar{
public volatile Boolean isInit = Boolean.FALSE;
static final AtomicReferenceFieldUpdater<MyVar, Boolean> referenceFieldUpdater =
AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");
public void init(){
try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
if (referenceFieldUpdater.compareAndSet(this, Boolean.FALSE, Boolean.TRUE)){
System.out.println(Thread.currentThread().getName() +"\t" + "start init...");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("over init...");
}else {
System.out.println("-------------------------");
}
}
}
class MyNumber{
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus(){
atomicInteger.getAndIncrement();
}
}
class BankAccount{
String bankName = "ICBC";
// AtomicReferenceFieldUpdater要求这个字段必须使用public volatile 修饰
public volatile int money = 0;
// 使用静态newUpdater方法创建一个更新器,并且需要设置想要更新的类和属性
static final AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
public void add(){
money++;
}
public void transMoney(){
fieldUpdater.getAndIncrement(this);
}
}
/**
* 统计四种方法的性能
*/
class ClickNumber{
int number = 0;
public synchronized void clickBySynchronized(){
number++;
}
public AtomicLong atomicLong = new AtomicLong();
public void clickByAtomicLong(){
atomicLong.getAndIncrement();
}
public LongAdder longAdder = new LongAdder();
public void clickByLongAdder(){
longAdder.increment();
}
public LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left + right;
}
}, 0);
public void clickByLongAccumulator(){
longAccumulator.accumulate(1);
}
}