поток java синхронизирован и блокировка не влияет?

#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();
    ...
 

Если вы достаточно глубоко вчитаетесь в любой из других ответов здесь, то поймете, почему это помогает.