Rust Guide > Documentation > Concurrency > AtomicI64

Introduction

In Rust, AtomicI64 is an atomic integer type that provides thread-safe operations on 64-bit signed integers. It is part of the standard library's std::sync::atomic module and is useful for shared state between threads where you need to perform atomic operations without using locks.

Using AtomicI64

To use AtomicI64, you need to import it from the std::sync::atomic module. You can create an AtomicI64 by using the AtomicI64::new method. Atomic operations such as store, load, fetch_add, and compare_and_swap can be used to manipulate the value safely across threads.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(5);

    println!("Initial value: {}", atomic.load(Ordering::Relaxed));

    atomic.store(10, Ordering::Relaxed);

    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10

Key Methods

Below are some of the key methods exposed by the AtomicI64 class:

AtomicI64::new

Creates a new AtomicI64 with the given initial value.

use std::sync::atomic::AtomicI64;

fn main() {
    let atomic = AtomicI64::new(10);
    println!("Created AtomicI64 with initial value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Created AtomicI64 with initial value: 10

load

Returns the current value of the AtomicI64.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(20);
    println!("Current value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Current value: 20

store

Sets the value of the AtomicI64.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(30);
    atomic.store(40, Ordering::Relaxed);
    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Updated value: 40

compare_and_swap

Compares the current value with current, and if they are equal, sets it to new.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(50);
    let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
    println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60

fetch_add

Adds to the current value, returning the previous value.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(70);
    let previous = atomic.fetch_add(10, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80

fetch_sub

Subtracts from the current value, returning the previous value.

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(100);
    let previous = atomic.fetch_sub(20, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80

Example Usage

Example 1: Basic Usage

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(5);

    println!("Initial value: {}", atomic.load(Ordering::Relaxed));

    atomic.store(10, Ordering::Relaxed);

    println!("Updated value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Initial value: 5
// Updated value: 10

Example 2: Using compare_and_swap

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(50);

    let old_value = atomic.compare_and_swap(50, 60, Ordering::Relaxed);
    println!("Old value: {}, New value: {}", old_value, atomic.load(Ordering::Relaxed));
}
// Output:
// Old value: 50
// New value: 60

Example 3: Using fetch_add

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(70);

    let previous = atomic.fetch_add(10, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 70
// New value: 80

Example 4: Using fetch_sub

use std::sync::atomic::{AtomicI64, Ordering};

fn main() {
    let atomic = AtomicI64::new(100);

    let previous = atomic.fetch_sub(20, Ordering::Relaxed);
    println!("Previous value: {}, New value: {}", previous, atomic.load(Ordering::Relaxed));
}
// Output:
// Previous value: 100
// New value: 80

Example 5: Using with Multiple Threads

use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::thread;

fn main() {
    let atomic = Arc::new(AtomicI64::new(0));

    let atomic1 = Arc::clone(&atomic);
    let handle1 = thread::spawn(move || {
        atomic1.fetch_add(1, Ordering::Relaxed);
    });

    let atomic2 = Arc::clone(&atomic);
    let handle2 = thread::spawn(move || {
        atomic2.fetch_add(1, Ordering::Relaxed);
    });

    handle1.join().unwrap();
    handle2.join().unwrap();

    println!("Final value: {}", atomic.load(Ordering::Relaxed));
}
// Output:
// Final value: 2

Considerations

  • Operations on AtomicI64 are lock-free and thread-safe.
  • Always use the appropriate Ordering when performing atomic operations to ensure the correct memory ordering guarantees.
  • Use AtomicI64 when you need to share a simple integer state across threads without the overhead of using a mutex.

See Also

  • AtomicUsize - An atomic integer type for shared state between threads.
  • Mutex - A mutual exclusion primitive for protecting shared data.
  • RwLock - A read-write lock allowing multiple readers or a single writer.