Java Academy Logo

Java Academy

Variables and Data Types in Java

A beginner-friendly guide to Java variables, data types, and how to share values safely between threads.

Part 1

Variables

A variable is a named place in memory that stores a value.

What is a variable?

In Java, every variable has a type, a name, and often a value. The type tells Java what kind of data it can store.

int age = 25;
String city = "Sydney";
boolean isActive = true;

Once you give a variable a type, you cannot put the wrong kind of data into it. For example, you cannot store text in an int.

Types of variables

Local

Inside a method. Lives only while the method runs. Must be given a value before use.

Instance

Inside a class, but outside methods. Each object has its own copy.

Static

Shared by all objects of the class. One copy for the whole class.

Final

Cannot be changed after it is set. Useful for constants.

ThreadLocal

Each thread gets its own private copy. Threads do not share this value.

Part 2

Data Types

Data types tell Java what kind of value a variable can hold.

Two main groups

Primitive

Stores the real value directly. Examples: int, boolean, double.

Non-primitive (Reference)

Stores an address that points to an object. Examples: String, arrays, custom classes.

How values are stored in memory

Primitive

int x = 10;
  • x stores the number 10 directly
  • No object is created

Non-primitive

String name = new String("John");
  • name stores a reference (address)
  • The text "John" lives on the heap
Diagram showing Java primitive and non-primitive data types
Primitive vs non-primitive data types in Java

The 8 primitive types

Java has eight primitive data types. They are the basic building blocks for other types.

TypeMeaningSizeExample
booleantrue / falseJVM dependenttrue
byteSmall whole number1 byte10
shortMedium whole number2 bytes2000
intCommon whole number4 bytes1000
longLarge whole number8 bytes123L
floatDecimal number4 bytes3.14f
doublePrecise decimal8 bytes3.14159
charSingle character2 bytes'A'

Non-primitive (reference) types

  • String — text
  • Arrays — list of values
  • Wrapper classes — Integer, Double, Boolean
  • Custom classes — classes you create
String title = "Java Guide";
int[] scores = {90, 85, 78};
Integer count = Integer.valueOf(42);

Part 3

Shared Variables (Threads)

When many threads use the same variable, you need special tools so the values stay correct.

The problem with a normal variable

Two threads may update the same number at the same time. This can give the wrong result.

int count = 0;

// Thread A and Thread B both do:
count++;   // NOT safe

// count++ is really 3 steps:
// 1. read count
// 2. add 1
// 3. write count back

If both threads read 0 at the same time, both may write 1. One update is lost. This is called a race condition.

Atomic variables

An atomic variable updates a value in one complete step. No other thread can interrupt in the middle. They live in java.util.concurrent.atomic.

Common atomic classes

ClassHoldsUse for
AtomicIntegerintCounters, IDs
AtomicLonglongLarge counters
AtomicBooleanbooleanFlags, start/stop
AtomicReferenceObjectShared object swap

AtomicInteger example

import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger visits = new AtomicInteger(0);

visits.incrementAndGet();  // safe ++
int total = visits.get();  // read value

Useful methods

  • get() — read the value
  • set(value) — set a new value
  • incrementAndGet() — add 1 safely
  • compareAndSet(old, new) — change only if value is still old

AtomicBoolean example

AtomicBoolean started = new AtomicBoolean(false);

// Only the first thread starts the service
if (started.compareAndSet(false, true)) {
    System.out.println("Starting...");
}

How CAS works (simple)

CAS means Compare And Swap. Before updating, it checks the value:

  1. Look at the current value
  2. Decide the new value
  3. Update only if nobody else changed it
  4. If someone else changed it, try again

Example: value is 5, thread wants 6. CAS says: "If it is still 5, change it to 6." If another thread already made it 7, skip and try again.

The volatile keyword

volatile means: when one thread changes this variable, other threads see the new value quickly.

volatile boolean running = true;

// One thread
running = false;

// Other threads see running == false soon

What it does

  • Shares the latest value
  • Good for stop flags

What it does NOT do

  • Does not make count++ safe
  • Does not replace AtomicInteger

Which one should you use?

Use the tool that matches your problem:

volatile — show the latest value

One thread writes. Other threads only read.

volatile boolean running = true;

Best for: stop flags, on/off switches

Atomic — safe update for one value

Many threads change the same number.

visits.incrementAndGet();

Best for: counters, IDs

synchronized — only one thread at a time

Several steps must stay together.

synchronized void transfer(...) {
    from.balance -= amount;
    to.balance += amount;
}

Best for: money transfer, updating many fields together

ToolSimple meaningExample
volatileShare the latest valueStop flag
AtomicSafely change one valueVisit counter
synchronizedOne thread runs a group of stepsMoney transfer

Quick rule: use volatile for a flag, use atomic for one shared number, use synchronized when many related changes must happen together.

Part 4

ThreadLocal Variables

ThreadLocal gives each thread its own private copy of a value.

What is ThreadLocal?

A normal shared variable is seen by all threads. A ThreadLocal variable is different: each thread has its own separate value. One thread cannot see or change another thread's ThreadLocal value.

Simple picture

Think of a locker room. Every person (thread) has their own locker (ThreadLocal value). They use the same locker room name, but each locker holds different things.

How to use ThreadLocal

ThreadLocal<String> userName = new ThreadLocal<>();

// Thread A
userName.set("Alice");
System.out.println(userName.get()); // Alice

// Thread B (different thread)
userName.set("Bob");
System.out.println(userName.get()); // Bob

// Alice's value is still Alice in Thread A
// Bob's value is still Bob in Thread B
  • set(value) — store a value for the current thread
  • get() — read this thread's value
  • remove() — delete this thread's value

ThreadLocal vs shared variables

Shared variable

  • All threads see the same value
  • Needs sync / atomic / volatile when updated
  • Example: visit counter

ThreadLocal

  • Each thread has its own value
  • No need to synchronize that value
  • Example: current user, request id

When to use ThreadLocal

  • Store data that belongs to one request/thread only
  • Keep a user id or transaction id for the current thread
  • Avoid passing the same value through many method calls
class UserContext {
    private static final ThreadLocal<String> CURRENT_USER =
            new ThreadLocal<>();

    static void setUser(String name) {
        CURRENT_USER.set(name);
    }

    static String getUser() {
        return CURRENT_USER.get();
    }

    static void clear() {
        CURRENT_USER.remove(); // important!
    }
}

Important: always call remove() when the work is finished (especially with thread pools). If you forget, an old value may stay and leak into the next task that reuses the same thread.

Part 5

FAQ

Is AtomicInteger a primitive type?

No. It is a class that holds an int and updates it safely. The 8 primitives are still byte, short, int, long, float, double, char, and boolean.

Can atomic variables replace synchronized?

Only for simple one-value updates. If you must change several fields together, use synchronized.

Is volatile enough instead of AtomicInteger?

No. volatile only helps other threads see the latest value. It does not make count++ safe.

What is a ThreadLocal variable?

ThreadLocal gives each thread its own private copy of a value. Other threads cannot see it. Always call remove() when finished, especially with thread pools.

Are atomic variables slower?

A little slower than a plain int with one thread. With many threads, they are often safer and cleaner for counters.

Continue Learning