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