一段JAVA代码了解多线程,JUC、CAS原子性操作。
@Test public void testPaceController_multiThread() throws InterruptedException { final PaceController paceController = new PaceController(1000, 160d); final Node node = mock(Node.class); final AtomicInteger passcount = new AtomicInteger(); final AtomicInteger blockcount = new AtomicInteger(); final AtomicInteger done = new AtomicInteger(); AtomicLong lastTm = new AtomicLong(System.currentTimeMillis() / 1000); int count = 1000; final CountDownLatch countDown = new CountDownLatch(count); for (int i = 0; i < count; i++) { Thread thread = new Thread(new Runnable() { @Override public void run() { for(int j =0; j< 10; j++) { boolean pass = paceController.canPass(node, 1).isPass(); if (pass == true) { passcount.incrementAndGet(); } else { blockcount.incrementAndGet(); } done.incrementAndGet(); long now = System.currentTimeMillis() / 1000; if (lastTm.get() != now) { System.out.println("pass:" + passcount.get() + ", tm:" + lastTm.get()); System.out.println("block:" + blockcount.get() + ", tm:" + lastTm.get()); System.out.println("done:" + done.get() + ", tm:" + lastTm.get()); passcount.set(0); blockcount.set(0); done.set(0); } lastTm.set(now); } countDown.countDown(); } }, "Thread " + i); thread.start(); } countDown.await(); System.out.println("pass:" + passcount.get() + ", tm:" + lastTm.get()); System.out.println("block:" + blockcount.get() + ", tm:" + lastTm.get()); System.out.println("done:" + done.get() + ", tm:" + lastTm.get()); }
1.CountDownLatch 同步并发处理
countDown.countDown 递减为0,等待发射信号。
countDown.await()阻塞当前线程,等待调用。
2.AtomicInteger 和 volatile 的区别,CAS原子性操作。
volatile关键字很重要的两个特性:
1、保证变量在线程间可见,对volatile变量所有的写操作都能立即反应到其他线程中,换句话说,volatile变量在各个线程中是一致的(得益于java内存模型—"先行发生原则");
2、禁止指令的重排序优化;
所以volatile 并非原子性操作。
AtomicInteger非阻塞同步(原子性CAS)
同步:多线程并发访问共享数据时,保证共享数据再同一时刻只被一个或一些线程使用。
我们知道,阻塞同步和非阻塞同步都是实现线程安全的两个保障手段,非阻塞同步对于阻塞同步而言主要解决了阻塞同步中线程阻塞和唤醒带来的性能问题,那什么叫做非阻塞同步呢?在并发环境下,某个线程对共享变量先进行操作,如果没有其他线程争用共享数据那操作就成功;如果存在数据的争用冲突,那就才去补偿措施,比如不断的重试机制,直到成功为止,因为这种乐观的并发策略不需要把线程挂起,也就把这种同步操作称为非阻塞同步(操作和冲突检测具备原子性)。在硬件指令集的发展驱动下,使得 "操作和冲突检测" 这种看起来需要多次操作的行为只需要一条处理器指令便可以完成,这些指令中就包括非常著名的CAS指令(Compare-And-Swap比较并交换)。《深入理解Java虚拟机第二版.周志明》第十三章中这样描述关于CAS机制:
图取自《深入理解Java虚拟机第二版.周志明》13.2.2
所以再返回来看AtomicInteger.incrementAndGet()方法,它的时间也比较简单
/** * Atomically increments by one the current value. * * @return the updated value */ public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
incrementAndGet()方法在一个无限循环体内,不断尝试将一个比当前值大1的新值赋给自己,如果失败则说明在执行"获取-设置"操作的时已经被其它线程修改过了,于是便再次进入循环下一次操作,直到成功为止。这个便是AtomicInteger原子性的"诀窍"了,继续进源码看它的compareAndSet方法:
/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
可以看到,compareAndSet()调用的就是Unsafe.compareAndSwapInt()方法,即Unsafe类的CAS操作。