钊钊 發表於 2026-1-12 08:50:58

解决Java中线程安全问题

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>线程安全问题的核心原因</li><li>线程安全问题的解决方案</li><li>方案1:避免共享可变资源(优先推荐)</li><li>方案2:同步/加锁(控制并发访问)</li><li>互斥同步(阻塞同步):这是最常见的方案,通过加锁来保证同一时刻只有一个线程操作资源。</li><li>ReentrantLock(显式锁):比synchronized灵活(可中断、可超时、公平锁),需手动释放锁(必须在finally中)。</li><li>方案3:volatile关键字(保证可见性/有序性)</li></ul></div><p class="maodian"></p><h3>线程安全问题的核心原因</h3>
<ul><li>线程安全问题本质是多个线程并发访问共享且可变的资源时,操作的原子性、可见性或有序性被破坏,导致程序执行结果不符合预期。</li></ul>
<ul><li>根本原因:共享可变资源</li></ul>
<ul><li>共享资源:多个线程都能访问到的资源(如成员变量、静态变量、共享内存区域);</li><li>可变资源:资源的状态(值)可以被修改(如int计数器、HashMap的元素);</li><li>经典的i++ 操作。它在底层分为&ldquo;读取-修改-写入&rdquo;三个步骤。如果两个线程同时读取 i=1,各自加1后写回,结果是2而不是3。</li></ul>
<ul><li>直接原因:三大特性被破坏</li></ul>
<ul><li>Java内存模型(JMM)定义的多线程并发三大核心特性,任何一个被破坏都会引发线程安全问题:<ul><li>原子性:一个操作(如count++)包含&ldquo;读 - 改 - 写&rdquo;三步,非原子操作会被多线程交错执行;</li><li>可见性:线程修改共享变量后,不会立即同步到主内存,其他线程读取的仍是旧值;</li><li>有序性:JVM的指令重排序优化,会导致多线程下执行顺序混乱(如未加volatile的双重检查锁单例)。</li></ul></li></ul>
<p class="maodian"></p><h3>线程安全问题的解决方案</h3>
<ul><li>核心思路:要么避免共享可变资源(从根源消除问题),要么控制并发访问规则(保证三大特性)。</li></ul>
<p class="maodian"></p><h3>方案1:避免共享可变资源(优先推荐)</h3>
<ul><li>栈封闭(局部变量):局部变量存储在线程私有栈中,每个线程有独立副本,天然线程安全。</li></ul>
<div class="jb51code"><pre class="brush:java;">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 &lt; 10; i++) {
            new Thread(demo::calculate, "Thread-" + i).start();
      }
    }
}</pre></div>
<ul><li>不可变对象:对象创建后状态不可修改(如String、Integer),即使共享也无法修改值。</li></ul>
<div class="jb51code"><pre class="brush:java;">// 自定义不可变类(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; }
}</pre></div>
<ul><li>ThreadLocal(线程本地存储):为每个线程提供独立的变量副本,线程操作自身副本,互不干扰。</li></ul>
<div class="jb51code"><pre class="brush:java;">public class ThreadLocalDemo {
    // 每个线程有独立的Integer副本,初始值为0
    private static ThreadLocal&lt;Integer&gt; threadLocal = ThreadLocal.withInitial(() -&gt; 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 &lt; 3; i++) {
            new Thread(() -&gt; {
                for (int j = 0; j &lt; 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</pre></div>
<p class="maodian"></p><h3>方案2:同步/加锁(控制并发访问)</h3>
<p class="maodian"></p><h3>互斥同步(阻塞同步):这是最常见的方案,通过加锁来保证同一时刻只有一个线程操作资源。</h3>
<ul><li>synchronized 关键字:Java 原生支持,使用简单。可修饰方法或代码块。属于不可中断的锁。</li></ul>
<div class="jb51code"><pre class="brush:java;">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 &lt; 1000; i++) {
            new Thread(demo::increment).start();
      }
      Thread.sleep(1000);
      System.out.println("最终count:" + demo.count); // 输出1000
    }
}</pre></div>
<p class="maodian"></p><h3>ReentrantLock(显式锁):比synchronized灵活(可中断、可超时、公平锁),需手动释放锁(必须在finally中)。</h3>
<div class="jb51code"><pre class="brush:java;">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 &lt; 1000; i++) {
            new Thread(demo::increment).start();
      }
      Thread.sleep(1000);
      System.out.println("最终count:" + demo.count); // 输出1000
    }
}</pre></div>
<p class="maodian"></p><h3>方案3:volatile关键字(保证可见性/有序性)</h3>
<ul><li>保证可见性:强制失效工作内存,直接读写主内存。</li><li>保证有序性:禁止指令重排序。</li><li>注意:它不保证原子性(不能解决i++问题)。</li></ul>
<div class="jb51code"><pre class="brush:java;">public class VolatileDemo {
    private volatile boolean stop = false; // 保证可见性和有序性
    public void runThread() {
      new Thread(() -&gt; {
            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; // 修改后,线程立即停止
    }
}</pre></div>
<p>到此这篇关于Java中线程安全问题的原因和解决方案的文章就介绍到这了,更多相关java线程安全内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>Java中的线程安全集合CopyOnWriteArrayList解析</li><li>Java多线程之线程安全问题详细解析</li><li>Java中的线程安全问题详细解析</li><li>Java AQS 线程安全同步队列的实现</li><li>Java线程安全之volatile详解</li><li>Java解决线程安全的两种方式分享</li><li>Java线程安全中的有序性浅析</li><li>浅谈java线程状态与线程安全解析</li><li>Java单例模式中的线程安全问题</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 解决Java中线程安全问题