簡體   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