You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中用Bakery算法避免死锁:程序偶发卡顿问题排查

问题:Bakery算法实现偶发卡顿,计数器结果不稳定

我实现了Bakery算法(封装在Bakery类中),用于保护Counter类的临界区(基于该类创建线程)。当前问题是程序有时会卡住,但有时能正常运行(此时计数器变量为20000,因为代码中有4个线程)。Solution是一个仅包含entrySection和exitSection方法的接口。

实现代码

public class Main {
 public static void main(String[] args) throws InterruptedException {
     int threadNumbers = 4;
     
     Bakery solution = new Bakery(threadNumbers);

     ArrayList<Counter> threads = new ArrayList<Counter>();

     for(int i =0; i<threadNumbers;i++){
         threads.add(new Counter(i,solution));
     }
     for(int i =0; i<threadNumbers;i++){
         threads.get(i).start();
     }
     for(int i =0; i<threadNumbers;i++){
         
         threads.get(i).join();
     }
     System.out.println(Counter.counter);
 }
}
public class Counter extends Thread{
    static int counter;
    int id;
    private final Bakery solution;
    int count = 0;

    public Counter(int id,Bakery solution) {
        this.id= id;
        this.solution = solution;
    }
    @Override
    public void run(){
        for (int i=0;i<5000;i++){
            this.solution.entrySection(this);
             counter++;
             System.out.println(counter);
            this.solution.exitSection(this);

        }

    }
}
public class Bakery implements Solution{
    boolean[] Choosing;
    int[] Numbers;
    Bakery(int n){
        Choosing = new boolean[n];
        Numbers = new int[n];
        for(int i=0;i<n; i++) {
            Choosing[i] = false;
            Numbers[i] = 0;
        }
    }
    @Override
    public void entrySection(Counter c) {
        Choosing[c.id] = true;
        int max = Numbers[0];
        for (int i = 1; i < Numbers.length; i++) {
            int currentNumber = Numbers[i];
            if (currentNumber > max) {
                max = currentNumber;
            }
        }
        Numbers[c.id] = max + 1;
        Choosing[c.id] = false;
        for(int i=0;i<Numbers.length; i++) {
            while(Choosing[i]){}
            while ((Numbers[i] != 0) && ((Numbers[i] < Numbers[c.id]) || ((Numbers[i] == Numbers[c.id]) && (i < c.id)))) {}
        }
    }
    @Override
    public void exitSection(Counter c) {
        Numbers[c.id] = 0;
    }
}

问题原因分析

  • 共享变量可见性缺失:Choosing和Numbers数组没有用volatile修饰,线程无法及时感知其他线程对这些变量的修改。比如某个线程修改了Choosing[i]为false,其他线程可能一直读取到旧的true值,导致while(Choosing[i])无限循环,程序卡住。
  • 计数器变量可见性隐患:Counter类中的static int counter未加volatile,主线程可能无法及时看到所有线程更新后的最终值,不过这不是卡顿的核心原因。

解决方案

核心是确保共享变量的线程可见性,修改如下:

1. 修改Bakery类的数组修饰符

给Choosing和Numbers数组添加volatile,确保线程间变量修改的即时可见:

public class Bakery implements Solution{
    volatile boolean[] Choosing;
    volatile int[] Numbers;
    Bakery(int n){
        Choosing = new boolean[n];
        Numbers = new int[n];
        for(int i=0;i<n; i++) {
            Choosing[i] = false;
            Numbers[i] = 0;
        }
    }
    @Override
    public void entrySection(Counter c) {
        Choosing[c.id] = true;
        int max = Numbers[0];
        for (int i = 1; i < Numbers.length; i++) {
            int currentNumber = Numbers[i];
            if (currentNumber > max) {
                max = currentNumber;
            }
        }
        Numbers[c.id] = max + 1;
        Choosing[c.id] = false;
        for(int i=0;i<Numbers.length; i++) {
            while(Choosing[i]){}
            while ((Numbers[i] != 0) && ((Numbers[i] < Numbers[c.id]) || ((Numbers[i] == Numbers[c.id]) && (i < c.id)))) {}
        }
    }
    @Override
    public void exitSection(Counter c) {
        Numbers[c.id] = 0;
    }
}

2. 优化Counter类的计数器变量

给static int counter添加volatile,确保主线程能及时获取最终计数结果:

public class Counter extends Thread{
    static volatile int counter;
    int id;
    private final Bakery solution;
    int count = 0;

    public Counter(int id,Bakery solution) {
        this.id= id;
        this.solution = solution;
    }
    @Override
    public void run(){
        for (int i=0;i<5000;i++){
            this.solution.entrySection(this);
             counter++;
             System.out.println(counter);
            this.solution.exitSection(this);

        }

    }
}

说明

添加volatile后,线程每次读取变量时都会直接从主内存获取最新值,写入时也会立即刷新到主内存,避免了因缓存不一致导致的无限循环问题,程序就能稳定运行并得到预期的20000结果。

内容的提问来源于stack exchange,提问作者Oualid Laib

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 18:15:29