Потокобезопасная глобальная переменная в 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. Может кто-нибудь объяснить мне, почему это так? И как сделать эту глобальную переменную потокобезопасной?

532   2  

2 ответов:

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

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

Вы получаете одно и то же значение каждый раз, потому что каждый из ваших Runnable s имеет свой собственный экземпляр класса 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;
    }
    ...

Comments

    Ничего не найдено.