Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the methods in AtomicInteger

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "what are the methods in AtomicInteger". In daily operation, I believe that many people have doubts about the methods in AtomicInteger. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful for you to answer the doubts about "what are the methods in AtomicInteger?" Next, please follow the editor to study!

Learn about AtomicInteger

AtomicInteger is a new utility class added by JDK1.5. Let's first take a look at its inheritance relationship.

Like int's wrapper class Integer, it inherits from the Number class.

This Number class is a wrapper class for basic data types, and objects related to data types generally inherit from the Number class.

Its inheritance system is very simple. Let's take a look at its basic properties and methods.

Basic properties of AtomicInteger

There are three basic properties of AtomicInteger

Unsafe is the class under the sun.misc package. AtomicInteger mainly relies on some native methods provided by sun.misc.Unsafe to ensure the atomicity of the operation.

The objectFieldOffset method of Unsafe can get the offset of the address of the member property in memory relative to the memory address of the object. To put it simply, it is to find the address of the variable in memory, so that it can be operated directly through the memory address later. This value is value, which we will talk about later.

Value is the value of AtomicIneger.

The Construction method of AtomicInteger

Looking further down, there are only two construction methods for AtomicInteger, one is the non-parameter construction method, the default value initial value for the no-parameter construction method is 0, and the construction method with parameters can specify the initial value.

Methods in AtomicInteger

Let's talk about the methods in AtomicInteger.

Get and Set

Let's first take a look at the simplest get and set methods:

Get (): gets the current AtomicInteger value

Set (): sets the current AtomicInteger value

Get () can read the data in AtomicInteger atomically, and set () can set the current value atomically, because get () and set () ultimately act on value variables, while value is modified by volatile, so get and set are equivalent to reading and setting memory. As shown in the following figure

We mentioned above about the non-atomic operations of iTunes + and iTunes +, and we said that we could use the methods in AtomicInteger to replace them.

Incremental operation

The Incremental related methods in AtomicInteger can meet our needs.

GetAndIncrement (): atomically increases the current value and returns the result. An operation equivalent to iTunes +.

To verify that it is thread safe, let's test it with the following example

Public class TAtomicTest implements Runnable {AtomicInteger atomicInteger = new AtomicInteger (); @ Override public void run () {for (int I = 0polii)

< 10000;i++){ System.out.println(atomicInteger.getAndIncrement()); } } public static void main(String[] args) { TAtomicTest tAtomicTest = new TAtomicTest(); Thread t1 = new Thread(tAtomicTest); Thread t2 = new Thread(tAtomicTest); t1.start(); t2.start(); } } 通过输出结果你会发现它是一个线程安全的操作,你可以修改 i 的值,但是最后的结果仍然是 i - 1,因为先取值,然后再 + 1,它的示意图如下。 incrementAndGet 与此相反,首先执行 + 1 操作,然后返回自增后的结果,该操作方法能够确保对 value 的原子性操作。如下图所示 Decremental 操作 与此相对,x-- 或者 x = x - 1 这样的自减操作也是原子性的。我们仍然可以使用 AtomicInteger 中的方法来替换 getAndDecrement : 返回当前类型的 int 值,然后对 value 的值进行自减运算。下面是测试代码 class TAtomicTestDecrement implements Runnable{ AtomicInteger atomicInteger = new AtomicInteger(20000); @Override public void run() { for(int i = 0;i < 10000 ;i++){ System.out.println(atomicInteger.getAndDecrement()); } } public static void main(String[] args) { TAtomicTestDecrement tAtomicTest = new TAtomicTestDecrement(); Thread t1 = new Thread(tAtomicTest); Thread t2 = new Thread(tAtomicTest); t1.start(); t2.start(); } } 下面是 getAndDecrement 的示意图 decrementAndGet:同样的,decrementAndGet 方法就是先执行递减操作,然后再获取 value 的值,示意图如下 LazySet方法 volatile 有内存屏障你知道吗? 内存屏障是啥啊? 内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是 CPU 或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。也是一个让CPU 处理单元中的内存状态对其它处理单元可见的一项技术。 CPU 使用了很多优化,使用缓存、指令重排等,其最终的目的都是为了性能,也就是说,当一个程序执行时,只要最终的结果是一样的,指令是否被重排并不重要。所以指令的执行时序并不是顺序执行的,而是乱序执行的,这就会带来很多问题,这也促使着内存屏障的出现。 语义上,内存屏障之前的所有写操作都要写入内存;内存屏障之后的读操作都可以获得同步屏障之前的写操作的结果。因此,对于敏感的程序块,写操作之后、读操作之前可以插入内存屏障。 内存屏障的开销非常轻量级,但是再小也是有开销的,LazySet 的作用正是如此,它会以普通变量的形式来读写变量。 也可以说是:「懒得设置屏障了」 GetAndSet 方法 以原子方式设置为给定值并返回旧值。 它的源码就是调用了一下 unsafe 中的 getAndSetInt 方法,如下所示 就是先进行循环,然后调用 getIntVolatile 方法,这个方法我在 cpp 中没有找到,找到的小伙伴们记得及时告诉让我学习一下。 循环直到 compareAndSwapInt 返回 false,这就说明使用 CAS 并没有更新为新的值,所以 var5 返回的就是最新的内存值。 CAS 方法 我们一直常说的 CAS 其实就是 CompareAndSet 方法,这个方法顾名思义,就是 「比较并更新」 的意思,当然这是字面理解,字面理解有点偏差,其实人家的意思是先比较,如果满足那么再进行更新。 上面给出了 CAS Java 层面的源码,JDK 官方给它的解释就是 「如果当前值等于 expect 的值,那么就以原子性的方式将当前值设置为 update 给定值」,这个方法会返回一个 boolean 类型,如果是 true 就表示比较并更新成功,否则表示失败。 CAS 同时也是一种无锁并发机制,也称为 Lock Free,所以你觉得 Lock Free 很高大上吗?并没有。 下面我们构建一个加锁解锁的 CASLock class CASLock { AtomicInteger atomicInteger = new AtomicInteger(); Thread currentThread = null; public void tryLock() throws Exception{ boolean isLock = atomicInteger.compareAndSet(0, 1); if(!isLock){ throw new Exception("加锁失败"); } currentThread = Thread.currentThread(); System.out.println(currentThread + " tryLock"); } public void unlock() { int lockValue = atomicInteger.get(); if(lockValue == 0){ return; } if(currentThread == Thread.currentThread()){ atomicInteger.compareAndSet(1,0); System.out.println(currentThread + " unlock"); } } public static void main(String[] args) { CASLock casLock = new CASLock(); for(int i = 0;i < 5;i++){ new Thread(() ->

{try {casLock.tryLock (); Thread.sleep (10000);} catch (Exception e) {e.printStackTrace ();} finally {casLock.unlock ();}}) .start () }

In the above code, we build a CASLock. In the tryLock method, we first use the CAS method to update, throw an exception if the update is not successful, and set the current thread as a locked thread. In the unLock method, we first determine whether the current value is 0, if it is 0, which is the result we want to see, and return it directly. Otherwise, 1 means that the current thread is still locked. Let's determine whether the current thread is a locked thread, and if so, perform the unlock operation.

So the compareAndSet we mentioned above can actually be parsed into the following operations

/ / pseudo code / / current value int v = 0; int a = 0; int b = 1; if (compare (0mem0) = = true) {set (0mem1);} else {/ / continue to execute}

You can also take the ticket purchase in the life scene as an example. If you travel to the scenic spot, you must have a ticket to enter. If you have a fake ticket or a ticket that does not conform to the scenic spot, you can definitely be identified. If you do not take the ticket, you will certainly not be able to enter the scenic spot.

Cut the crap, this is the schematic diagram of compareAndSet.

WeakCompareAndSet: damn it, I watched it very carefully for several times, and found that this method of JDK1.8 is exactly the same as that of compareAndSet.

But is that really the case? Not really, JDK source code is very broad and profound, will not design a repetitive method, you think the JDK team will not commit such a low-level team, but what is the reason?

The book "Java High concurrency detailed explanation" gives us an answer.

AddAndGet

AddAndGet and getAndIncrement, getAndAdd, incrementAndGet and other methods all use do. The while + CAS operation is actually equivalent to a spin lock. If the CAS modification is successful, it will be looped all the time, and the modification failure will be returned. The schematic diagram is as follows

Go deep into AtomicInteger

We discussed the specific use of AtomicInteger above, and we know that AtomicInteger relies on volatile and CAS to ensure atomicity, so let's analyze why CAS can guarantee atomicity, what is its underlying layer, and what is the relationship between AtomicInteger and optimistic locks?

The underlying implementation principle of AtomicInteger Let's take a look at this lovely compareAndSetL (CAS) method. Why are these two lines of code guaranteed atomicity?

We can see that this CAS method is equivalent to calling the compareAndSwapInt method in unsafe. Let's go into unsafe Fang Fa to see the specific implementation.

CompareAndSwapInt is a method in sun.misc, this method is a native method, and its underlying layer is implemented by Cmax Candle +, so we need to look at the source code of Cmax Cure +.

Do you know the power of Cmax Clipper +? To use Java is to play with applications and architectures, while Cmax + is to play with servers and low-level ones.

The source code of compareAndSwapInt is in the jdk8u-dev/hotspot/src/share/vm/prims/unsafe.app path, and its source code implementation is

That's the Unsafe_CompareAndSwapInt method, and we found this method.

I don't understand the source code, either, but this doesn't prevent us from finding the key code Atomic::cmpxchg, which is the assembly instruction of the x86 CPU architecture and its main function is to compare and exchange operands. Let's move on to find the definition of this instruction.

We will find that the underlying implementation of different os is different.

We found the way to implement Windows as follows

Let's keep looking down, and it actually defines line 216 of the code, and we look in it.

At this point, knowledge related to assembly instructions and registers is needed.

The os::is-MP () above is the interface of the multiprocessing operating system, and below is _ _ asm, which is the keyword of C _ assembler +, which is used to call the inline assembler.

The code in _ _ asm is an assembler, which roughly puts the values of dest, exchange_value and compare_value in registers. The following code in LOCK_IF_MP roughly means

If it is multiprocessor, lock is performed, and then the comparison operation is performed. Where cmp represents comparison, mp represents MultiProcess,je for equality jump, and L0 represents the identification bit.

When we go back to the assembly instructions above, we can see that the bottom layer of CAS is the cmpxchg instruction.

Optimistic lock

Do you have any questions about why AtomicInteger can get the current value, and why there are inconsistencies between expectValue and value?

Because AtomicInteger is just an atomic utility class, it is not exclusive, it is not as mutually exclusive and exclusive as synchronized or lock, remember that there are two methods get and set in AtomicInteger? They are only decorated with volatile, and volatile is not atomic, so there may be inconsistencies in the current values of expectValue and value, so repeated modifications may occur.

There are two solutions to the above situation. One is to use similar locking mechanisms such as synchronized and lock, which is exclusive, that is, only one thread can modify it at a time. This way can guarantee atomicity, but it is relatively expensive. This lock is also called pessimistic lock. Another solution is to use the version number or the CAS method.

"version number"

The version number mechanism is implemented by adding a version field to the data table, indicating the number of times the data has been modified. When the write operation is performed and the write is successful, version = version + 1. When thread A wants to update the data, it will also read the version value while reading the data. When submitting the update, if the version value just read is equal to the version value in the current database, otherwise, the update operation will be retried until the update is successful.

"CAS method"

Another way is CAS, we have used a lot of space to introduce the CAS method, so we think you now have some understanding of its operating mechanism, we will not elaborate on its operating mechanism.

Everything has its advantages and disadvantages. There is no perfect solution in the software industry, only the best solution, so optimistic lock also has its weaknesses and defects, that is, the ABA problem.

ABA problem

What the ABA problem says is that if the value of a variable read for the first time is A, and when it is ready to write to A, it is found that the value is still A, in this case, can it be considered that the value of A has not been changed? It could be the case of A-> B-> A, but AtomicInteger doesn't think so. It only believes what it sees, and what it sees is what it sees. For example,

If you now have a single linked list, as shown in the following figure

A.next = B, B.next = null, at this time, two threads T1 and T2 respectively take A from the single linked list. Due to some special reasons, T2 changes A to B, and then to A. At this time, T1 executes the CAS method and finds that the single linked list is still A, and the CAS method will be executed. Although the result is correct, this operation will cause some potential problems.

At this point, it is still a single linked list. Two threads T1 and T2 respectively take A from the single linked list, and then T1 changes the linked list to ACD as shown in the following figure

At this point, T2, if you find that the memory value is still A, you will try to replace the value of A with B, because the reference to B is null, which will cause C and D to be free.

The AtomicStampedReference class after JDK 1.5 provides this capability, in which the compareAndSet method first checks whether the current value is equal to the expected value, which is determined by the fact that the current reference and postmark are equal to the expected reference and postmark, respectively, and if they are all equal, they are set to the given update value atomically.

All right, here's the Java code flow. When we see native, we know we're going to play cpp again. Start playing.

The simple explanation is that UnsafeWrapper is a wrapper, just another name. Then after some JNI processing, because compareAndSwapOject compares references, it needs to go through C++ object-oriented transformation. The most important method is atomic_compare_exchange_oop

As you can see, there is the familiar word cmpxchg, which means that compareAndSwapOject still uses cmpxchg atomic instructions, but it has gone through a series of transformations.

At this point, the study of "what are the methods in AtomicInteger" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report