AtomicFU: boxing free atomics that still read like plain values in Kotlin
The idiomatic way to use atomic operations in Kotlin
At a glance
- What is it?
- AtomicFU is a multiplatform Kotlin library for atomic operations that ships a compiler plugin, so the atomic you write as a boxed value is transformed into a plain field updated through the JVM field updaters, into an intrinsic on Native, and into a plain value on JS, while Wasm keeps the boxed runtime. It suits anyone writing lock-free code in Kotlin who wants the stdlib shape without the allocation, and the plugin is still marked Beta.
- Who is it for?
- Adopt AtomicFU if you write lock-free Kotlin and want the standard library atomic shape without the per-object allocation on the JVM, because the plugin rewrites the atomic into a plain field with a field updater and gives you the idiomatic update and loop extensions on top. Do not adopt it in a project that cannot move to Gradle 8.2 and Kotlin 2.2, because the current plugin requires both and the older plugin versions are the fallback.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the library is for
AtomicFU is a multiplatform library that provides the idiomatic and efficient way of using atomic operations in Kotlin, and it is a JetBrains official project published to Maven Central as org.jetbrains.kotlinx.atomicfu.
The problem it solves is a familiar one in Kotlin. The standard library gives you atomic integers and atomic references, and they are objects. A counter in a hot loop allocates, and the reference type you reach for when you want a general atomic value is a boxed one. The library's answer is stated as a single line in the feature list: code it like a boxed value with atomic(0), but run it in production efficiently.
So the code you write is the same shape whether you are on the JVM, on Native, in a browser or in WebAssembly, and a compiler plugin rewrites it per platform.
The example in the README is a lock-free stack, and it shows the whole API surface. You declare the head with the atomic function, and the value property gives you volatile reads and writes.
private val top = atomic<Node?>(null)
fun isEmpty() = top.value == null // volatile read
fun clear() { top.value = null } // volatile writeThere is compareAndSet for the cases where you need to check and set yourself, there are the higher level primitives, and there are the integer and long variants, which are declared with type inference, so atomic(0) and atomic(0L), and which support the usual getAndIncrement, incrementAndGet, getAndAdd and addAndGet operations, plus atomic modification through the plus equals and minus equals operators.
The licence is Apache 2.0 according to the badge in the README, although the repository licence field is recorded as one that GitHub could not classify, so the LICENSE.txt at the root is the reference.
What the transformation does on each platform
The per platform behaviour is the substance of the library, and the feature list states it precisely.
For the JVM, an atomic value is represented as a plain value that is atomically updated with the AtomicXxxFieldUpdater classes from the Java standard library. So the field in your object stops being a reference to a wrapper and becomes the value itself, with the field updater doing the compare and swap under the hood. No allocation, no indirection, and the memory semantics come from the JDK rather than from a Kotlin implementation.
For JS, an atomic value is represented as a plain value. JavaScript has no real atomics in the general case, so the target here is a single threaded environment where the plain value is sufficient, and the transformation removes the wrapper.
For Native, atomic operations are delegated to the Kotlin/Native atomic intrinsics, which map onto the platform's own atomic instructions, and that is the target where the primitives matter most.
For Wasm, an atomic value is not transformed. It remains boxed, and the kotlinx-atomicfu library is used as a runtime dependency. That is the honest limitation of the approach, since the WebAssembly threading proposal is still not universally available, and the design does not pretend otherwise.
The full multiplatform support covers the JVM, Native, JS and Wasm, with Wasm added since Kotlin 1.9.20.
The idiomatic layer: loop, update and updateAndGet
The library is not only a transformation, it also provides a set of Kotlin specific extensions, and those extensions are what make the code pleasant.
The example shows a loop primitive that reads the current value volatilely and repeats, which is the shape of a compare and set loop written once instead of at every call site.
top.loop { cur -> // while(true) loop that volatile-reads current value
...
}And then the higher level update functions, which the documentation recommends whenever possible for idiomatic lock-free code.
fun push(v: Value) = top.update { cur -> Node(v, cur) }
fun pop(): Value? = top.getAndUpdate { cur -> cur?.next } ?.valueThe difference between using the raw primitives and using these is visible in the two lines above. A push with a raw compare and set is a loop with a retry and a local variable, and a pop is the same shape again. With update and getAndUpdate, the intent is a single expression and the retry is inside the library.
The feature list also names atomic arrays, user-defined extensions on atomics and locks, and tracing operations for debugging, and there is a section on each later in the document.
One small but important detail in the example: the integer and long variants note the initial value, so atomic(0) and atomic(0L) rather than a single atomic(0) that would infer the wrong type, and that is because the numeric atomics have their own specialised representation rather than the general boxed one.
Applying the plugin, and the plugin id change
The setup depends on your build system, and there is one change to note before you copy anything.
Starting from version 0.25.0 the plugin id is org.jetbrains.kotlinx.atomicfu, and the README puts that in a note of its own because the old id is what most existing configurations use.
plugins {
id("org.jetbrains.kotlinx.atomicfu") version "0.33.0"
}The Groovy form is the same thing with the older syntax.
plugins {
id 'org.jetbrains.kotlinx.atomicfu' version '0.33.0'
}The legacy application is still documented, for projects that apply the plugin through a buildscript classpath rather than the plugins block.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:0.33.0")
}
}
apply(plugin = "org.jetbrains.kotlinx.atomicfu")The version requirements are stated for the current plugin, and they are floors rather than suggestions. Gradle 8.2 or newer, and Kotlin 2.2.0 or newer, with a pointer to the previous plugin versions if your project cannot meet them.
There is one constraint that is easy to miss and that the README calls out separately. The atomicfu Gradle plugin only relies on the version of the Kotlin Gradle Plugin present in your project, and that matters if your project configures a custom Kotlin compiler version or modifies the Kotlin Native compiler version through the kotlin.native.version property. The plugin has to see the same Kotlin the project is built with, and a project that pins a different compiler will hit a mismatch.
Maven, and the two flavours of the transformation
Maven configuration is supported for JVM projects, and it comes with a detail that is easy to get wrong.
You declare the version as a property, and then the dependency on the atomicfu library itself, with the scope set to provided.
<properties>
<atomicfu.version>0.33.0</atomicfu.version>
</properties><dependencies>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>atomicfu</artifactId>
<version>${atomicfu.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>The provided scope is there because the transformation removes the runtime need for the library on the JVM, so shipping it would add a dependency that the compiled code never uses.
The two flavours are the compiler plugin and the legacy bytecode transformation, and they need different Maven configurations depending on which you choose. The compiler plugin has to be downloaded as a jar file to be applied to the project, and it can be configured with the Maven Dependency Plugin, with the note that the compiler plugin version should match the Kotlin plugin version applied.
That last point is the one to remember. The transformation is a compiler plugin, so the plugin version has to line up with the Kotlin version in the build, which is the same constraint expressed from the Maven side.
Arrays, locks, tracing and the runtime dependency question
The more features section is where the library goes beyond a single atomic value.
There is support for arrays of atomic values, which is a case the standard library does not cover, since a fixed size array of atomics is a natural thing to want for a per shard counter.
There is support for user-defined extensions on atomics, which means you can add your own operations to the atomic type, such as a compare and swap loop that does something particular, and the extension participates in the transformation rather than boxing the value to implement it.
There is a locks section, and there is tracing operations for debugging. The tracing is the interesting one for a library like this, because the whole point of the transformation is that the value is no longer a visible object, and therefore you cannot inspect it in a debugger. Tracing operations give you a way to see what happened to the atomic without turning the transformation off.
On the Wasm target, the library is used as a runtime dependency, since the value is not transformed there. So the size of the artifact you ship depends on the target, and on the JVM, where the transformation is complete, the library is a compile time dependency only.
The Kotlin/Native support has its own section, and the requirement note about the Kotlin Native compiler version applies to it as much as to the rest.
The Beta caveat and what an upgrade costs
The status badge at the top says Kotlin Beta, and the note underneath it is specific about what that means.
The plugin is in its active development phase and changes from release to release. The project does provide compatibility of atomicfu transformed artifacts between releases, but does not provide strict compatibility guarantees on the plugin API and its general stability between Kotlin versions.
So there are two different kinds of compatibility, and the distinction is worth holding on to. If you publish a library that is compiled with AtomicFU, consumers of that library can use your artifacts across AtomicFU releases. What is not guaranteed is that the plugin API itself is stable, which means the configuration surface in your build script is the part that may change when you move versions.
In practice that means the upgrade cost lands in the build files, not in the source. A project that has the plugin version, the atomicfu library version and the Kotlin version all in step can move to a new AtomicFU release with a version bump, and the risk is a configuration option that was renamed or a requirement that moved. It is the kind of dependency that is cheap to adopt and cheap to keep current, as long as nobody expects the plugin API to behave like a stable public API.
The repository layout reflects the implementation modules, with the atomicfu library, the Gradle plugin, the Maven plugin and the transformer in separate modules, plus an integration-testing directory and a build settings logic project. The version is 0.33.0, released in May 2026, with the two before it in March, and the last push to the repository was on 2026-10-07.
Editorial conclusion
Adopt AtomicFU if you write lock-free Kotlin and want the standard library atomic shape without the per-object allocation on the JVM, because the plugin rewrites the atomic into a plain field with a field updater and gives you the idiomatic update and loop extensions on top. Do not adopt it in a project that cannot move to Gradle 8.2 and Kotlin 2.2, because the current plugin requires both and the older plugin versions are the fallback. Before you upgrade the plugin, read the Beta note, since the transformed artifacts stay compatible between releases but the plugin API itself carries no stability guarantee across Kotlin versions.
Frequently asked questions
What does the AtomicFU compiler plugin actually change?
On the JVM an atomic value becomes a plain value updated atomically with the AtomicXxxFieldUpdater classes from the Java standard library. On JS it becomes a plain value, on Native the operations are delegated to the Kotlin/Native atomic intrinsics, and on Wasm the value is not transformed and stays boxed with the library as a runtime dependency.
What are the requirements for the AtomicFU Gradle plugin?
Gradle 8.2 or newer and Kotlin 2.2.0 or newer. The plugin only relies on the Kotlin Gradle Plugin version present in your project, which matters if you configure a custom Kotlin compiler version or change the Kotlin Native compiler version through the kotlin.native.version property.
How do I apply the AtomicFU plugin in Gradle?
Add the plugins block entry with the id org.jetbrains.kotlinx.atomicfu and the version you want, for example id("org.jetbrains.kotlinx.atomicfu") version "0.33.0". The plugin id changed at version 0.25.0, and the legacy buildscript classpath application is still documented for existing configurations.
Is AtomicFU stable enough to depend on?
It is marked as a Kotlin Beta project. The README notes that the plugin is in active development and changes between releases, and while compatibility of transformed artifacts between releases is provided, there are no strict compatibility guarantees on the plugin API or its general stability between Kotlin versions.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kotlin-kotlinx-atomicfu)Was this useful?