Потокобезопасная глобальная переменная в 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(вместо того, чтобы строить его). Затем, есть основной метод создания один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; } ...
Comments