Java 同步方法和 block

标签 java concurrency synchronization synchronized concurrent-programming

我试图更全面地了解 Java 中多线程的同步。我了解使用synchronized关键字背后的高级思想,以及它如何在线程之间提供互斥。

唯一的事情是,即使您删除了synchronized关键字,我在网上和教科书中阅读的大多数示例仍然可以正常工作,这使得这个主题比我想象的更加令人困惑。

任何人都可以为我提供一个具体的例子,说明何时不包含同步关键字会产生错误的结果?任何信息将不胜感激。

最佳答案

您通常可以通过增加迭代次数来触发竞争条件。这是一个简单的示例,可以进行 100 次和 1,000 次迭代,但在 10,000 次迭代(有时)时失败(至少在我的四核机器上)。

public class Race
{
    static final int ITERATIONS = 10000;
    static int counter;

    public static void main(String[] args) throws InterruptedException {
        System.out.println("start");
        Thread first = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < ITERATIONS; i++) {
                    counter++;
                }
            }
        });
        Thread second = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < ITERATIONS; i++) {
                    counter++;
                }
            }
        });
        first.start();
        second.start();
        first.join();
        second.join();
        System.out.println("Counter " + counter + " should be " + (2 * ITERATIONS));
    }
}

>>> Counter 12325 should be 20000

此示例失败,因为对 counter 的访问未正确同步。它可能会以两种方式失败,可能在同一次运行中都失败:

  • 一个线程无法看到另一个线程已递增计数器,因为它没有看到新值。
  • 一个线程在另一个线程读取当前值和写入新值之间递增计数器。这是因为递增和递减运算符不是原子的。

这个简单程序的修复方法是使用 AtomicInteger。由于增量的问题,使用 volatile 是不够的,但是 AtomicInteger 提供了增量、获取和设置等原子操作。

关于Java 同步方法和 block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13021256/

相关文章:

java - 处理 "killed"Java作业的方法?

java - CompletableFuture - 提供一个将 bool 值返回给 SupplyAsync 调用的方法

java - 使用 spring 入站文件适配器进行并发处理

java - 如何在 Java 中比较字符串?

java - 保留大数组以加快访问速度

java - Collections.synchronizedList() 方法有什么用?它似乎没有同步列表

stream - cudaStreamWaitEvent 似乎没有等待

java - 并发访问对 HashSet 的影响

Java死代码在不同的地方

java - 如何使用 FFmpeg 限制阅读速度