主要参考java
https://www.jianshu.com/p/fb6e91b013cc算法
package volatileTest; public class CounterIPlusPlus { int i = 0; public static void main(String[] args) throws InterruptedException { System.out.println("Class Name:" + Thread.currentThread().getStackTrace()[1].getClassName()); System.out.println("JDK version:" + System.getProperty("java.version")); Long s = System.currentTimeMillis(); final CounterIPlusPlus c = new CounterIPlusPlus(); // TODO Auto-generated method stub for (int j = 0; j < 100; j++) { new Thread(new Runnable() { public void run() { for (int k = 0; k < 1000; k++) { c.add(); } } }).start(); } while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield(); System.out.println("spend time:" + (System.currentTimeMillis() - s)); System.out.println("result:" + c.i); } public void add() { ++i; } }
Class Name:volatileTest.CounterIPlusPlusatom
JDK version:1.7.0_79spa
spend time:22操作系统
result:99367线程
Class Name:volatileTest.CounterIPlusPlus3d
JDK version:1.7.0_79code
spend time:39blog
result:100000生命周期
Class Name:volatileTest.CounterIPlusPlus
JDK version:1.7.0_79
spend time:17
result:98962
package volatileTest; public class CounterSynchronized { public int i = 0; public static void main(String... strings) { System.out.println("Class Name:" + Thread.currentThread().getStackTrace()[1].getClassName()); System.out.println("JDK version:" + System.getProperty("java.version")); Long s = System.currentTimeMillis(); final CounterSynchronized t = new CounterSynchronized(); for (int i = 0; i < 100; i++) { new Thread() { public void run() { for (int j = 0; j < 1000; j++) { t.increase(); } } }.start(); } while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield(); System.out.println("spend time:" + (System.currentTimeMillis() - s)); System.out.println("result:" + t.i); } public synchronized void increase() { i++; } }
Class Name:volatileTest.CounterSynchronized
JDK version:1.7.0_79
spend time:26
result:100000
Class Name:volatileTest.CounterSynchronized
JDK version:1.7.0_79
spend time:44
result:100000
Class Name:volatileTest.CounterSynchronized
JDK version:1.7.0_79
spend time:39
result:100000
100个线程,能够想象有100把锁。每一个线程操做时,都会加锁(让其余线程没法操做)。实际上此时100个线程的执行已经不是并行执行了,已经变成了串行执行。
synchronized 每次加锁解锁都涉及上下文切换,须要涉及操做系统级别的操做,因此是重量级的操做。
package volatileTest; import java.util.concurrent.atomic.AtomicInteger; public class CounterCAS { private AtomicInteger i = new AtomicInteger(0); public static void main(String... strings) { System.out.println("Class Name:" + Thread.currentThread().getStackTrace()[1].getClassName()); System.out.println("JDK version:" + System.getProperty("java.version")); Long s = System.currentTimeMillis(); final CounterCAS c = new CounterCAS(); for (int j = 0; j < 100; j++) { new Thread(new Runnable() { public void run() { for (int k = 0; k < 1000; k++) { c.increment(); } } }).start(); } while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield(); System.out.println("spend time:" + (System.currentTimeMillis() - s)); System.out.println("result:" + c.i.get()); } public void increment() { for (;;) { int a = i.get(); if (i.compareAndSet(a, a + 1)) { break; } } } }
Class Name:volatileTest.CounterCAS
JDK version:1.7.0_79
spend time:19
result:100000
Class Name:volatileTest.CounterCAS
JDK version:1.7.0_79
spend time:20
result:100000
Class Name:volatileTest.CounterCAS
JDK version:1.7.0_79
spend time:20
result:100000
加锁(使用synchronized)能够实现同步操做。
使用CAS,使用AtomicInteger的 incrementAndGet 方法也能够实现同步,原理呢?
CPU中指令集支持【Compare And Swap】这种原子性操做,好比Intel的cmpxchg指令。
100条线程同时【Compare And Swap】操做时,只有一个线程能真正成功的进行CAS操做,而后其余线程都会获取CAS操做失败的信号(获取操做失败信号的线程就会什么也不作,这些线程至关于空转了一回)。
线程太多,直接计算机就卡死了,亲测。
while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield();
synchronized操做i++,实质上并行操做已经变成了串行操做。至于线程A每次抢到锁,加锁后是作了1次i++操做就解锁,仍是连续作了10次i++操做再解锁,不知道,都有可能。这些也都不重要了,由于总共100 X 1000次i++操做至关因而一个一个顺序执行的,因此最终数字不会出错。
synchronized操做,实际上每一个线程都是足足执行了1000次自增,哪一个线程也没少干活,只不过各个线程都再争抢先干完,结束本身的1000次自增任务。详细见后面《CounterSynchronizedDetail.java》的说明。
public void increment() { for (;;) { int a = i.get(); if (i.compareAndSet(a, a + 1)) { break; } } }
CAS操做i++,没有线程的切换,全部线程都是一直在运行,只不过某个线程在作循环CAS操做时,若是一直【捕捉不到本身的备份数据跟内存数据一致的状况】时,就只好一直空转了,可是每一个线程的每次自增操做不走空,啥意思?就是必需要成功进行一次自增操做才会跳出循环。而某次循环时幸运地成功【捕捉到本身的备份数据跟内存数据一致】,则在成功执行自增操做后进入下一次外层的for循环【for (int k = 0; k < 1000; k++)】。
循环CAS操做就比如不少人(实际上这里是线程)在抢活儿干,每次任务都是进行自增操做。可是每次的活儿(就是自增操做)只有一我的(实际上这里是线程)能成功抢到,其余人没抢到只好继续抢下一次机会。并且每一个人(实际上这里是线程)不管抢活儿的能力如何,都要完成1000次的自增,因此早干完(1000次循环)早休息,晚干完晚休息。并且每次使用【for (;;)】抢活,抢不到的话线程就不会跳出【for (;;)】,必定要在里面抢到一次活儿才会跳出来进行下次外层的for循环【for (int k = 0; k < 1000; k++)】。
CAS算法中实际上每一个线程也都足足执行1000次自增,每一个线程也都是没少干活,各个线程也都是在争取先完成本身的1000次自增,而后早点结束本身的1000次自增任务。
CAS中为何不能有两个线程同时抢到自增执行权呢?(以Intel为例)由于CAS最底层依赖的是CPU的cmpxchg原子操做,若是A线程成功的完成了cmpxchg操做(线程A进行cmpxchg操做时硬件机制能保证其余线程没法进行cmpxchg操做),那么内存数据就改变且立刻全部线程均可以看到最新的内存数据了。其余线程再作cmpxchg操做时,发现本身手里的备份数据已经和如今内存的数据不一致了,cmpxchg操做失败,立马进入下一次循环,争取下一次能成功进行cmpxchg操做。
因此CAS最终仍然是依靠原子操做保证每次自增只有一个线程能成功执行,与synchronized不一样的是CAS操做不涉及加锁解锁和线程间的切换。synchronized是在代码层经过synchronized原语自己保证的;而CAS算法的原子操做时依赖于底层CPU硬件的指令(好比Intel中是cmpxchg)保证的。
抽象的说,就是:每次只有一个线程能真正成功的进行CAS操做,而后其余线程都会获取CAS操做失败的信号,获取操做失败信号的线程就会什么也不作,这些线程至关于空转了一回,而后转入本身的下一循环看是否可以成功地进行一次CAS操做。
package volatileTest; public class CounterSynchronizedDetail { public int i = 0; public static void main(String... strings) { System.out.println("Class Name:" + Thread.currentThread().getStackTrace()[1].getClassName()); System.out.println("JDK version:" + System.getProperty("java.version")); Long s = System.currentTimeMillis(); final CounterSynchronizedDetail t = new CounterSynchronizedDetail(); for (int i = 0; i < 100; i++) { new Thread() { public void run() { for (int j = 0; j < 1000; j++) { t.increase(); } } }.start(); } while (Thread.activeCount() > 1) // 保证前面的线程都执行完 Thread.yield(); System.out.println("spend time:" + (System.currentTimeMillis() - s)); System.out.println("result:" + t.i); } public synchronized void increase() { System.out.println(Thread.currentThread().getName()); i++; } }
将打印结果拷贝到notepad中。
从行号能够看到多打印了四行,剩下的100000行都是线程名Thread-XX。说明全部线程一共执行了100000次。
进行计数,Thread-0以下,能够看到是1000次匹配。
Thread-1呢?(注意要选择【全词匹配】)也是1000次匹配,每一个线程都是如此。
因此说虽然线程们争先恐后的抢夺锁的控制权,实际上那个线程都没少干活,抢来抢去都是为了早点完成本身的任务而后结束本身全部的for循环操做。而后呢?若是是线程池,那么线程完成全部任务就休息了,等待下次有任务来再次被调度。若是就是单个线程,那么线程就结束生命周期了。