简体   繁体   English

并发中的意外行为 (Java)

[英]Unexpected behavior in concurrency (Java)

I have written the simplest source to reproduce the problem as follows我已经编写了最简单的源代码来重现问题,如下所示

package concurrency.test;

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class Main
{
    public static void main(String[] args)
    {
        new Main().start();
    }

    private void start()
    {
        for (int i = 0; i < 20; i++)
        {
            Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new SequencePrinter(), 1, 1, TimeUnit.SECONDS);
        }
    }

    private class SequencePrinter implements Runnable
    {
        @Override
        public void run()
        {
            System.out.println( IdGenerator.instance().nextId());
        }
    }

    private static class IdGenerator
    {
        private static IdGenerator instance;
        private final AtomicLong idSequence = new AtomicLong( 0 );

        private IdGenerator()
        {
        }

        public static IdGenerator instance ()
        {
            if ( instance == null )
            {
                instance = new IdGenerator();
            }

            return instance;
        }

        synchronized public long nextId ()
        {
            return idSequence.incrementAndGet();
        }
    }
}

What I expect: Unique ids in no order我的期望:无序的唯一 ID

What I have found: Multiple 1s (any other number is unique but not '1')我发现了什么:多个 1(任何其他数字都是唯一的,但不是“1”)

It looks like I have not understood some basic concepts of concurrency.看来我还没有理解并发的一些基本概念。 Can you tell me what am I doing wrong?你能告诉我我做错了什么吗?

The instance() method of your class IdGenerator is not thread-safe, so multiple instances of this class might be created, each with their own idSequence member variable, when multiple threads call the method concurrently.您的类IdGeneratorinstance()方法不是线程安全的,因此当多个线程同时调用该方法时,可能会创建此类的多个实例,每个实例都有自己的idSequence成员变量。

You'll need to make the instance() method thread-safe, for example by making it synchronized .您需要使instance()方法成为线程安全的,例如通过使其成为synchronized

What is the point of synchronizing synchronized increment?同步同步增量有什么意义? Next time, always, use static Type singlenote = new ... to create singletones.下一次,总是使用static Type singlenote = new ...来创建static Type singlenote = new ...

import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

public class SequencePrinter implements Runnable {

    public void run() {
        System.out.println("produces " + idSequence.incrementAndGet());
    }

    public static void main(String[] args) {

        for (int i = 0; i < 200; i++)
            Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                new SequencePrinter(), 1, 10, TimeUnit.SECONDS);
    }

    private static final AtomicLong idSequence = new AtomicLong( 0 );

}

The code becomes much simpler and buts eliminated.代码变得更简单,但被淘汰了。 Might be you wanted to create instance in the getNext and that is why you sunchronized it?可能您想在 getNext 中创建实例,这就是您对其进行同步的原因? Do not create tons of useless getter methods.不要创建大量无用的 getter 方法。

This should do it.这应该这样做。 The check-then-act in the instance method is a race condition, which needs to be atomic. instance方法中的 check-then-act 是一个竞争条件,它需要是原子的。

if (instance == null) {
    instance = new IdGenerator();                
}

Secondly, the idSequence is an atomicLong, so it doesn't need to be synchronized.其次, idSequence是一个atomicLong,所以不需要同步。

private static class IdGenerator
    {
        private static IdGenerator instance;
        private final AtomicLong idSequence = new AtomicLong( 0 );
        private static Object lock = new Object();

        private IdGenerator()
        {
        }

        public static IdGenerator instance ()
        {
            synchronized (lock) {
                if (instance == null) {
                    instance = new IdGenerator();
                }
            }
            return instance;
        }

        public long nextId ()
        {
            return idSequence.incrementAndGet();
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM