上一章节我们讲了 volatile的可见性的,以及可见性的演示,这里就会给大家产生一个误区,这样的使用方式很容易给人的感觉是
    对volatile修饰的变量进行并发操作是线程安全的。 其实不然,用volatile修饰的变量只有两个特性就是 可见性、禁止指令重排序。并不能保证线程的安全性
    我们通过以下代码进行演示。

    1. volatile int iCounter = 0;
    2. AtomicInteger atomicInteger = new AtomicInteger();
    3. public static void main(String[] args) throws InterruptedException {
    4. LatchTest latchTest = new LatchTest();
    5. latchTest.startTaskAllInOnce(5);
    6. }
    7. private void m() throws InterruptedException {
    8. for (int i = 0; i < 1000000; i++) {
    9. iCounter++;
    10. atomicInteger.incrementAndGet();
    11. }
    12. }
    13. public void startTaskAllInOnce(int threadNums) throws InterruptedException {
    14. final CountDownLatch startGate = new CountDownLatch(1);
    15. final CountDownLatch endGate = new CountDownLatch(threadNums);
    16. for (int i = 0; i < threadNums; i++) {
    17. new Thread(() -> {
    18. try {
    19. System.out.println("wait thread");
    20. startGate.await();
    21. try {
    22. m();
    23. } finally {
    24. endGate.countDown();
    25. }
    26. } catch (InterruptedException ie) {
    27. ie.printStackTrace();
    28. }
    29. }).start();
    30. }
    31. System.out.println("thread waited");
    32. startGate.countDown();
    33. endGate.await();
    34. System.out.println("iCounter: " + iCounter + " atomicInteger :" + atomicInteger);
    35. }
    36. 输出结果:
    37. volatile iCounter: 2972488 atomicInteger :5000000
    38. volatile iCounter: 2737312 atomicInteger :5000000
    39. volatile iCounter: 3613868 atomicInteger :5000000
    40. volatile iCounter: 2081604 atomicInteger :5000000
    41. volatile iCounter: 2875711 atomicInteger :5000000
    42. volatile iCounter: 3037079 atomicInteger :5000000
    43. volatile iCounter: 2806466 atomicInteger :5000000
    44. volatile iCounter: 3218029 atomicInteger :5000000
    45. volatile iCounter: 2608899 atomicInteger :5000000
    46. volatile iCounter: 2513628 atomicInteger :5000000
    47. AtomicInteger 是原子性操作,线程安全的,volatile 并不能保证线程安全。

    这是因为虽然 **volatile 保证了内存可见性,每个线程拿到的值都是最新值,但 count++** 这个操作并不是原子的,这里面涉及到获取值、自增、赋值的操作并不能同时完成。

    • 所以想到达到线程安全可以使这三个线程串行执行(其实就是单线程,没有发挥多线程的优势)。
    • 也可以使用** synchronize **或者是锁的方式来保证原子性。
    • 还可以用 **Atomic 包中 AtomicInteger 来替换 int,它利用了 CAS** 算法来保证了原子性。