JAVA operating principle of atoms

When we solve concurrency problems, are often used java.util.concurrent.atomic package java, then what is in principle?

I. Introduction

Java atomic operation is dependent on the processor implementation, the processor providing a bus lock and lock caching mechanism to ensure the two complex atomic memory operations.

  • Using the bus key guarantee atomicity. The so-called bus key is to use a LOCK # signal provided by the processor, this processor output when a signal on the bus, requesting other processors will be blocked live, then the processor may be exclusively shared memory.
  • Using the cache lock guarantee atomicity. A so-called cache memory area if the locking means is cached in the cache line in the processor, and are locked during Lock operation, the lock operation performed when it is written back to memory, the processor is not claimed that LOCK # signal on the bus, and it is to modify the internal memory address, and allow it to cache coherency mechanism to ensure the atomic operation, because cache coherency mechanism stops at the same time to modify two or more data processor cache memory area, when other processors have been written back when the data cache line lock, will make the cache line is invalid.
问题:为什么要引入缓存锁定?
因为在某些情况下,我们需要保证对某个内存地址的操作是原子性即可,但是总线锁定把SPU和内存之间的通讯锁住了,这使得锁定期间,其他处理器不能操作其他内存地址的数据,所以总线锁定的开销比较大,目前处理器在某些场合下使用缓存锁定代替总线锁定来进行优化。
复制代码

Two, Java atomicity operation Introduction

In Java can be implemented by an atomic operation and locking manner CAS cycle. CAS in principle on a "JAVA realization of the principle of the locking mechanism (Starter Edition)" Introduction biased lock mentioned.

1. CAS cycle to achieve an atomic operation.

Let's look at a counter method safeCount based CAS thread-safe and non-safe example of the counter count.

public class CasAtomicInteger {
    private int i = 0;
    private AtomicInteger atomicI = new AtomicInteger(0);
    public static void main(String[] args) {
        final CasAtomicInteger cas = new CasAtomicInteger();
        List<Thread> ts = new ArrayList<>(600);
        long start = System.currentTimeMillis();
        for (int j = 0; j < 100; j ++) {
            Thread t = new Thread(() -> {
                for (int i = 0; i < 10000; i ++) {
                    cas.count();
                    cas.safeCount();
                }
            });
            ts.add(t);
        }

        for (Thread t : ts) {
            t.start();
        }

        for (Thread t : ts) {
            try {
                t.join();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("非线程安全count计数器:" + cas.i);
        System.out.println("线程安全safeCount计数器:" + cas.atomicI.get());
        System.out.println(System.currentTimeMillis() - start);
    }

    /**
     * 使用CAS实现线程安全计数器
     */
    private void safeCount() {
        for (;;) {
            int i = atomicI.get();
            boolean suc = atomicI.compareAndSet(i, ++i);
            if (suc) {
                break;
            }
        }
    }

    /**
     * 非线程安全计数器
     */
    private void count() {
        i++;
    }
}
复制代码

Data results:

非线程安全count计数器:998883
线程安全safeCount计数器:1000000
107
复制代码

Clearly, non-thread-safe count counter counting less. Here, we encounter a similar problem java.util.concurrent.atomic bag concurrency java tool can be used to solve. atomic atomic operation provides a number of packets, such as An AtomicBoolean is (updated atomically boolean value), of AtomicInteger (atomically updated int value), AtomicLong (updated atomically long value) and the like.

2. On the three issues CAS atomic operations

(1) ABA problem. Because CAS need when operating value, check the value has not changed, if not changed is updated, but if a value is A, became a B, then change back to the A, you will find that when using CAS inspection its value does not change, but actually changed.

Solution: Use the version number of each variable update when the version number plus 1. From Java1.5 start, JDK's Atomic package provides a class AtomicStampedReference to address the ABA problem. CompareAndSet action of this class is the method checks the current reference is equal to the expected reference and checks whether the current flag is equal to the expected flag, if all equal Atomically update value and the set value of the flag for a given reference.

(2) circulating a long time spending big. If the time is not successful spin CAS, it will bring a very large CPU execution cost. If the JVM can support pause instructions provided by the processor, so there will be some efficiency improvement.

pause command has two effects: first, it can delay the pipelined execution of instructions (de-pipeline), so that the CPU does not consume too many resources to perform, the delay time of implementation dependent, in some processor time delay it is zero; second, it can avoid the memory conflict sequence (memory order Violation) caused by CPU pipeline is cleared (CPU pipeline Flush), in order to improve the efficiency of the CPU when exiting the loop.

(3) can only guarantee atomic operation of a shared variable. When performing operations on a shared variable, we can use the CAS cycle approach to ensure an atomic operation, but when multiple shared variables operating cycle CAS can not guarantee atomic operations, this time you can use the lock.

Reference: "Java concurrent programming art" Fang Tengfei, Wei Peng HLA - A2

Guess you like

Origin juejin.im/post/5d0dac7c5188253298401919