#java #multithreading #concurrency #threadpool #synchronized
Вопрос:
так сильно запутался, почему я получаю случайный результат при выполнении «i » в синхронизированном или заблокированном методе?
public class aaa implements Runnable {
static int count = 0;
public static void main(String[] args) {
aaa aaa = new aaa();
aaa.create();
}
public void create() {
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 1000; i ) {
aaa thread = new aaa();
executor.execute(thread);
}
executor.shutdown();
while (true){
if(executor.isTerminated()){
System.out.println("a " count);
break;
}
}
}
@Override
public void run() {
this.test();
}
public void test() {
Lock lock = new ReentrantLock();
try {
lock.lock();
count ;
System.out.println(count);
} finally {
lock.unlock();
}
}
}
или:
public synchronized void test() {
count ;
System.out.println(count);
}
в результате получается случайное число иногда 1000, иногда 998, 999 …и т. Д., И печать внутри метода «тест» выполняется не в последовательности, а как :
867
836
825
824
821
820
819
817
816
a 999
Однако, если он находится в синхронизированном блоке, все выглядит хорошо:
public void test() {
synchronized (aaa.class) {
count ;
System.out.println(count);
}
}
результат:
993
994
995
996
997
998
999
1000
a 1000
Я думаю, что все вышеперечисленные методы должны дать мне один и тот же результат 1000, и приращение должно быть последовательным, но работает только последний метод.Что не так с кодом? Пожалуйста, помогите!!!
Ответ №1:
Вы создаете несколько экземпляров aaa, каждый экземпляр создает свою собственную блокировку входа, и каждый поток при выполнении плавно получает блокировку от своего собственного экземпляра.
public void test() {
Lock lock = new ReentrantLock();
try {
lock.lock();
count ;
System.out.println(count);
} finally {
lock.unlock();
}
}
Поскольку существует несколько экземпляров aaa, каждый поток выполняется на своем собственном экземпляре, и синхронизированный метод использует текущий объект aaa.class
public synchronized void test() {
count ;
System.out.println(count);
}
Причина получения надлежащего результата при таком подходе заключается в том, что вы используете aaa.class в качестве объекта синхронизации
public void test() {
synchronized (aaa.class) {
count ;
System.out.println(count);
}
}
Решение заключается в повторном использовании одной и той же блокировки(повторной блокировки) во всех потоках. Определение блокировки на том же уровне, что и количество переменных, решило бы проблему.
Ответ №2:
Вы должны создать один мьютекс, т. е.
static Lock lock = new ReentrantLock();
Ваш синхронизированный метод не работает, так как вы создаете N aaa
экземпляров, тогда каждый (нестатический) метод отличается (со своим собственным мьютексом).
Ваши synchronized (aaa.class)
работы с тех пор aaa.class
одинаковы Object
для всех aaa
экземпляров и методов.
Затем, если вам нужно синхронизировать метод, убедитесь, что он одинаков для всех потоков, например, если test
static
он будет одинаковым для всех
@Override
public void run() {
test();
}
public static synchronized void test() {
count ;
}
но вы можете ввести «класс счетчика», например
class Counter {
int count = 0;
// non static but synchronized for all (since they use the same `counter` object)
synchronized void inc() {
count ;
}
}
будет использоваться для всех потоков
...
SyncTest thread = new SyncTest(counter); // <== the same
...
(полный код)
public class SyncTest implements Runnable {
private final Counter c;
public SyncTest(Counter c) {
this.c = c;
}
static class Counter {
int count = 0;
// non static but synchronized for all (since they use the same `counter` object)
synchronized void inc() {
count ;
}
}
@Override
public void run() {
test();
}
public void test() {
this.c.inc();
}
public static void main(String[] args) {
// one counter for all
Counter counter = new Counter();
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 10000; i ) {
SyncTest thread = new SyncTest(counter);
executor.execute(thread);
}
executor.shutdown();
while (true) {
if (executor.isTerminated()) {
System.out.println("a " counter.count);
break;
}
}
}
}
Комментарии:
1. спасибо, сэр, поскольку синхронизированный метод не будет работать в этом случае, при каких обстоятельствах работает синхронизированный метод? Не могли бы вы привести пример?
2. @ee есть много способов сделать это, я обновил ответ двумя
Ответ №3:
Эмпирическое правило: Объявите переменную блокировки в следующей строке после переменных, которые вы хотите с ее помощью защитить, и объявите ее с теми же ключевыми словами. Например.,
public class aaa implements Runnable {
static int count = 0;
static Lock countLock = new ReentrantLock();
...
Если вы достаточно глубоко вчитаетесь в любой из других ответов здесь, то поймете, почему это помогает.