繁体   English   中英

并发中的意外行为 (Java)

[英]Unexpected behavior in concurrency (Java)

我已经编写了最简单的源代码来重现问题,如下所示

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();
        }
    }
}

我的期望:无序的唯一 ID

我发现了什么:多个 1(任何其他数字都是唯一的,但不是“1”)

看来我还没有理解并发的一些基本概念。 你能告诉我我做错了什么吗?

您的类IdGeneratorinstance()方法不是线程安全的,因此当多个线程同时调用该方法时,可能会创建此类的多个实例,每个实例都有自己的idSequence成员变量。

您需要使instance()方法成为线程安全的,例如通过使其成为synchronized

同步同步增量有什么意义? 下一次,总是使用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 );

}

代码变得更简单,但被淘汰了。 可能您想在 getNext 中创建实例,这就是您对其进行同步的原因? 不要创建大量无用的 getter 方法。

这应该这样做。 instance方法中的 check-then-act 是一个竞争条件,它需要是原子的。

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

其次, 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