Java 中的线程同步问题

Problem with thread synchronizing in Java

我很清楚这可能被认为是重复的,但是我 运行 考虑到我这里的问题,通过许多答案我无法提出解决方案。

我将我的 runnable 与一个由多个线程共享的对象同步,并显式同步了我在内部使用的方法,但程序的结果始终是 3000。

我尝试锁定 Counter class 但它不会改变任何事情。 谁能解释一下为什么我的操作 none 在这个特定示例中有效?

    public static void zad3() {
        var counter = new Counter();

        var toRun = new Runnable() {
            @Override
            public void run() {
                synchronized (counter) {
                    for (var i = 0; i < 1000; i++) {
                        counter.add(1);
                    }
                }
            }
        };

        var t1 = new Thread(toRun);
        var t2 = new Thread(toRun);
        var t3 = new Thread(toRun);

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("counter = " + counter.getCount());
   }
public class Counter {
    protected long count_ = 0;

    public synchronized void add(long value) {
        count_ += value;
    }

    public long getCount() {
        return count_;
    }
}

编辑: 正如所建议的那样,问题出在循环中,每个线程不断 运行 一次 1000 次。 我的解决方案:

        var toRun = new Runnable() {
            @Override
            public void run() {
                synchronized (counter) {
                    for (var i = counter.getCount(); i < 1000; i++) {
                        counter.add(1);
                    }
                }
            }
        };

您已经同步了围绕 "counter" 变量的完整 for 循环,这意味着每个线程将 运行 tthe 块一次。 3 X 1000 = 3000

这个块将每个线程执行一次

 for (var i = 0; i < 1000; i++) {
                        counter.add(1);
 }

更新:根据您的评论判断您想要中断 1000 示例代码可以是:

 t1.start();
 t2.start();
 t3.start();

while(counter.getValue()<1000) {
    Thread.sleep(20)
}

另一个建议:

public class Incremetor extends Runnable {
   Counter counter;

public Incremetor(Counter counter) {
    this.counter = counter;
}
public void run() {
   counter.increment();
}

}

ExecutorService executorService = Executors.newFixedThreadPool(8); // this mean 8 threads in total to do your runnables.
for (int i=0;i<1000;++i) {
     executorService.submit(new Incrementor(counter));        
}

所以问题是你让每个线程尝试 1000 次增量,所以你需要这样的东西:

while (counter.getCount() < 1000) {
     counter.add(1);
}

您提供的解决方案可能会给您正确的结果,但您实际上只是从 1 个线程递增计数器。当您使用 synchronized(object) { } 创建一个同步块时,所有线程都将尝试获取该块的锁,但只有一个线程会。这意味着在您的解决方案中,第一个获得锁的线程将执行所有 1000 个增量。当线程释放锁让其他人拿到锁时,工作就已经完成了。因此,实际上在 3 个线程之间分配增量的解决方案不应同步整个 for-loop.

如果你运行我建议的while-loop,你会得到更接近1000,但实际上可能超过1000。记得运行你的程序10次或设置一个 test-function 运行 100 次并报告回来。问题是,从读取 counter.getCount() 的角度来看,该值可能已经被另一个线程更改了。要可靠地 总是 得到 1000,您可以确保读取和写入计数器的专有权:

while (true) {
    synchronized (counter) {
        if (counter.getCount() < 1000) {
            counter.add(1);
        } else {
            break;
        }
    }
}

请注意,像这样递增 one 变量 很慢 。你只做 1000,但尝试 10 亿。事实上,3 线程版本(在我的 PC 上)需要 1 分钟 17 秒,而一个简单的顺序循环需要大约 1.2 秒。您可以通过在线程之间分配工作负载并让它们在具有独占权限的本地计数器上工作然后最后添加结果来解决这个问题。