Потокобезопасная глобальная переменная в Java

Я пытаюсь понять механизм безопасности потоков в java, и мне нужна помощь. У меня есть класс:

public class ThreadSafe {

    private Executor executor = new ScheduledThreadPoolExecutor(5);

    private long value = 0;

    public void method() {
        synchronized (this) {
            System.out.println(Thread.currentThread());
            this.value++;
        }
    }

    private synchronized long getValue() {
        return this.value;
    }

    public static void main(String... args) {
        ThreadSafe threadSafe = new ThreadSafe();
        for (int i = 0; i < 10; i++) {
            threadSafe.executor.execute(new MyThread());
        }

    }

    private static class MyThread extends Thread {

        private ThreadSafe threadSafe = new ThreadSafe();

        private AtomicBoolean shutdownInitialized = new AtomicBoolean(false);

        @Override
        public void run() {
            while (!shutdownInitialized.get()) {
                threadSafe.method();
                System.out.println(threadSafe.getValue());
            }
        }
    }

}

вот я пытаюсь сделать value потокобезопасный, доступ только к одному потоку за раз. Когда я запускаю эту программу, я вижу, что на value даже если я оберну его в synchronized блок. Конечно, этот цикл будет бесконечным, но это просто пример, я останавливаю эту программу вручную через несколько секунд, поэтому У меня:

2470
Thread[pool-1-thread-3,5,main]
2470
Thread[pool-1-thread-5,5,main]
2470
Thread[pool-1-thread-2,5,main]

различные потоки получают доступ и изменяют это value. Может кто-нибудь объяснить мне, почему это так? И как сделать эту глобальную переменную потокобезопасной?

2 ответов


каждый поток имеет свой собственный ThreadSafe и друг ThreadSafe имеет свои, разные value. Более того,synchronized методы блокировки на this, так что каждый ThreadSafe блокируется сам по себе-и ни один из них не разделяется между потоками. Это называется потоковой локальностью, и это самый простой способ обеспечить безопасность потока. :)

чтобы добраться до эксперимента, который, я думаю, вы хотите, вам нужно изменить MyThread такой, что его конструктор принимает ThreadSafe аргумент (вместо построения одного). Затем, есть основной метод create one ThreadSafe и друг MyThread во время строительства.


вы получаете то же значение каждый раз, потому что каждый из ваших RunnableS имеет свой собственный экземпляр ThreadSafe класса.

если вы хотите, чтобы они все разделяли один и тот же класс, вам понадобится только один экземпляр ThreadSafe и передайте его во все ваши задания - см. ниже. Как уже упоминалось,AtomicLong это путь, если вы хотите потокобезопасный общий long.

кроме того,MyThread класса необходимо не extend Thread. Он должен вместо этого implements Runnable. Ваш код работает, потому что Thread уже implements Runnable. Если вы сделали myThread.interrupt() на самом деле это не прерывание потока, потому что это потоки пула потоков, которые вызывают ваш run() метод.

что-то вроде следующего будет работать:

ThreadSafe threadSafe = new ThreadSafe();
for (int i = 0; i < 10; i++) {
    threadSafe.executor.execute(new MyRunnable(threadSafe));
}
...
private static class MyRunnable implements Runnable {
    private final ThreadSafe threadSafe;
    public MyRunnable(ThreadSafe threadSafe) {
       this.threadSafe = threadSafe;
    }
    ...