Java 线程安全与如何解决(java线程安全的几种方式)


在 Java 中,线程安全(Thread Safety)是指当多个线程同时访问某个类、对象或方法时,其行为始终符合预期,且不会出现数据不一致或逻辑错误。线程安全的核心是解决多线程环境下的 竞态条件(Race Condition)数据可见性 问题。


1. 线程不安全的表现

示例:线程不安全的计数器

public class UnsafeCounter {
    private int count = 0;

    public void increment() {
        count++; // 非原子操作:实际是 read -> modify -> write
    }

    public int getCount() {
        return count;
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        UnsafeCounter counter = new UnsafeCounter();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println("Final count: " + counter.getCount()); 
        // 预期 2000,实际可能小于 2000(如 1998)
    }
}

原因count++ 是非原子操作,多个线程可能同时读取旧值并覆盖写入。


2. 实现线程安全的 6 种方法

方法 1:使用synchronized同步

通过同步代码块或方法,保证同一时间只有一个线程访问共享资源。

public class SafeCounter {
    private int count = 0;

    public synchronized void increment() { // 同步方法
        count++;
    }

    public int getCount() {
        return count;
    }
}

方法 2:使用ReentrantLock

显式锁提供更灵活的同步控制(如尝试锁、超时锁)。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SafeCounter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

方法 3:使用原子类(Atomic Classes)

Java 提供
java.util.concurrent.atomic
包下的原子类(如 AtomicInteger),基于 CAS(Compare-And-Swap)实现无锁线程安全。

import java.util.concurrent.atomic.AtomicInteger;

public class SafeCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}

方法 4:使用不可变对象(Immutable Objects)

不可变对象天然线程安全,因为其状态在创建后不可修改。

public final class ImmutableUser {
    private final String name;
    private final int age;

    public ImmutableUser(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 没有 setter 方法
    public String getName() { return name; }
    public int getAge() { return age; }
}

方法 5:使用线程局部变量(ThreadLocal)

每个线程拥有独立的变量副本,避免共享。

public class ThreadLocalDemo {
    private static ThreadLocal<Integer> threadLocalCount = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                threadLocalCount.set(threadLocalCount.get() + 1);
            }
            System.out.println(Thread.currentThread().getName() + ": " + threadLocalCount.get());
        };

        new Thread(task).start();
        new Thread(task).start();
    }
}

方法 6:使用并发容器(Concurrent Collections)

Java 提供线程安全的容器类,如 ConcurrentHashMapCopyOnWriteArrayList

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentMapDemo {
    public static void main(String[] args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("key", 0);

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                map.compute("key", (k, v) -> v + 1); // 原子操作
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();
        System.out.println("Final value: " + map.get("key")); // 2000
    }
}

3. 线程安全的级别

  1. 1. 不可变(Immutable):对象状态不可修改(如 String)。
  2. 2. 绝对线程安全:所有操作线程安全(如 AtomicInteger)。
  3. 3. 相对线程安全:单个操作线程安全,但复合操作需外部同步(如 Vector)。
  4. 4. 线程兼容:需通过同步机制保证安全(如 ArrayList)。

4. 线程安全的注意事项

  1. 1. 避免共享变量:优先使用局部变量或线程封闭(如 ThreadLocal)。
  2. 2. 减少同步范围:同步代码块应尽量小,避免性能问题。
  3. 3. 注意锁的粒度:粗粒度锁可能降低并发性能。
  4. 4. 避免死锁:按顺序获取锁,或使用超时机制。
  5. 5. 使用并发工具类:优先选择 java.util.concurrent 包下的工具。

5. 常见线程不安全类及替代方案

非线程安全类线程安全替代方案
ArrayListCopyOnWriteArrayListHashMapConcurrentHashMapSimpleDateFormatDateTimeFormatter
(Java 8+)StringBuilderStringBuffer(同步方法)


通过合理选择同步策略和工具,可以高效解决 Java 多线程编程中的线程安全问题。

#java##JAVA##并发##安全##线程##java 编程#

原文链接:,转发请注明来源!