java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java线程安全

解决Java中线程安全问题

作者:我会替风去

线程安全问题源于多个线程并发访问共享可变资源,破坏了原子性、可见性或有序性,解决方法包括避免共享可变资源、使用同步/加锁机制以及volatile关键字,本文给大家介绍解决Java中线程安全问题,感兴趣的朋友跟随小编一起看看吧

线程安全问题的核心原因

线程安全问题的解决方案

方案1:避免共享可变资源(优先推荐)

public class StackClosedDemo {
    // 每个线程调用该方法时,都会创建独立的count副本
    public void calculate() {
        int count = 0;
        count++; // 无线程安全问题
        System.out.println(Thread.currentThread().getName() + ": " + count);
    }
    public static void main(String[] args) {
        StackClosedDemo demo = new StackClosedDemo();
        // 10个线程各自操作自己的局部变量
        for (int i = 0; i < 10; i++) {
            new Thread(demo::calculate, "Thread-" + i).start();
        }
    }
}
// 自定义不可变类(final类+final成员变量+无setter)
public final class ImmutableUser {
    private final String name;
    private final int age;
    public ImmutableUser(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 仅提供getter,无setter
    public String getName() { return name; }
    public int getAge() { return age; }
}
public class ThreadLocalDemo {
    // 每个线程有独立的Integer副本,初始值为0
    private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
    public void increment() {
        threadLocal.set(threadLocal.get() + 1);
        System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
    }
    public static void main(String[] args) {
        ThreadLocalDemo demo = new ThreadLocalDemo();
        // 3个线程各自操作自己的副本
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                for (int j = 0; j < 2; j++) {
                    demo.increment();
                }
            }, "Thread-" + i).start();
        }
    }
}
// 输出(顺序可能不同):
// Thread-0: 1、Thread-0: 2
// Thread-1: 1、Thread-1: 2
// Thread-2: 1、Thread-2: 2

方案2:同步/加锁(控制并发访问)

互斥同步(阻塞同步):这是最常见的方案,通过加锁来保证同一时刻只有一个线程操作资源。

public class SynchronizedDemo {
    private int count = 0;
    // 同步实例方法,锁是this对象
    public synchronized void increment() {
        count++;
    }
    public static void main(String[] args) throws InterruptedException {
        SynchronizedDemo demo = new SynchronizedDemo();
        // 1000个线程执行increment
        for (int i = 0; i < 1000; i++) {
            new Thread(demo::increment).start();
        }
        Thread.sleep(1000);
        System.out.println("最终count:" + demo.count); // 输出1000
    }
}

ReentrantLock(显式锁):比synchronized灵活(可中断、可超时、公平锁),需手动释放锁(必须在finally中)。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
    private int count = 0;
    private Lock lock = new ReentrantLock(); // 默认非公平锁
    public void increment() {
        lock.lock(); // 加锁
        try {
            count++;
        } finally {
            lock.unlock(); // 释放锁,避免死锁
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        for (int i = 0; i < 1000; i++) {
            new Thread(demo::increment).start();
        }
        Thread.sleep(1000);
        System.out.println("最终count:" + demo.count); // 输出1000
    }
}

方案3:volatile关键字(保证可见性/有序性)

public class VolatileDemo {
    private volatile boolean stop = false; // 保证可见性和有序性
    public void runThread() {
        new Thread(() -> {
            int i = 0;
            while (!stop) { // 能立即感知stop的修改
                i++;
            }
            System.out.println("线程停止,i=" + i);
        }).start();
    }
    public static void main(String[] args) throws InterruptedException {
        VolatileDemo demo = new VolatileDemo();
        demo.runThread();
        Thread.sleep(3000);
        demo.stop = true; // 修改后,线程立即停止
    }
}

到此这篇关于Java中线程安全问题的原因和解决方案的文章就介绍到这了,更多相关java线程安全内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文