Является ли lock.tryLock () поток безопасным? - PullRequest
1 голос
/ 19 октября 2019

Я попробовал программу гонки потоков с Synchronization, lock.lock () и lock.tryLock (), я обнаружил, что с синхронизацией и lock.lock () работает нормально, но сама lock.tryLock () не является поточно-ориентированной. Этот метод не может получить блокировку и, следовательно, дает неожиданные результаты


import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 10; i++) {


            Resources resources = new Resources();
            Thread userThread1 = new Increment(resources);
            Thread userThread2 = new Decrement(resources);
            userThread1.start();
            userThread2.start();
            userThread1.join();
            userThread2.join();

            System.out.println(resources.getCounter());
        }
    }

    private static abstract class UserThread extends Thread {
        protected Resources resources;

        public UserThread(Resources resources) {
            this.resources = resources;
        }

    }

    private static class Increment extends UserThread {

        public Increment(Resources resources) {
            super(resources);
        }

        public void run() {
            for (int i = 0; i < 10000; i++) {
                resources.increemnt();

            }

        }
    }

    private static class Decrement extends UserThread {

        public Decrement(Resources resources) {
            super(resources);
        }

        public void run() {

            for (int i = 0; i < 10000; i++) {
                resources.decrement();

            }

        }
    }

    private static class Resources {

        public ReentrantLock getLock() {
            return lock;
        }

        private ReentrantLock lock = new ReentrantLock();

        public int getCounter() {
            return counter;
        }

        private int counter = 0;

        public void increemnt() {
            if (lock.tryLock()) {
                try {
                    counter++;
                } finally {
                    lock.unlock();
                }

            }
        }

        public void decrement() {
            if (lock.tryLock()) {
                try {
                    counter--;
                } finally {
                    lock.unlock();
                }

            }
        }
    }

}

Ожидаемый: 0,0,0,0,0,0,0,0,0,0 Фактический результат: его случайный длякаждый пробег

1 Ответ

2 голосов
/ 20 октября 2019

Метод tryLock является поточно-ориентированным. Он делает то, что должен делать (в соответствии с javadoc) надежно.

Однако использование вами trylock приводит к не поточно-ориентированной реализации increment и * 1006. *. Если вы собираетесь использовать trylock в этой ситуации, вам нужно будет сделать что-то вроде этого:

try {
    while (!lock.tryLock()) { }  // Try to get the lock until you get it
    counter++;
} finally {
    lock.unlock();
}

Но это плохая идея, потому что вы эффективно заняты, ожидая блокировки. Лучшее решение с использованием Lock было бы:

try {
    lock.lock();  // Block until the lock is acquired.
    counter++;
} finally {
    lock.unlock();
}

Но если вы ищете решение без блокировки, то вам следует использовать атомарный тип:

  • java.util.concurrent.atomic.LongAdder ( javadoc ) для Java 8 и более поздних версий,
  • java.util.concurrent.atomic.AtomicLong ( javadoc ) всех версий Java, начиная с Java 5 включительно.

Очевидно, LongAdder работает лучше, если есть много споров:


Обратите внимание, что потокобезопасность на самом деле сложно определить точно. Вам нужно начать с поведенческой спецификации правильного поведения алгоритма. Тогда вы можете сказать, что реализация алгоритма.

Определение: если алгоритм корректен в соответствии со спецификацией при работе в системе с одним процессором, то он поточно-безопасный если оно также всегда правильно в соответствии со спецификацией при наличии нескольких процессоров.

Из этого мы видим, что:

  • thread-безопасность является спорной, если алгоритм не соответствует его поведенческой спецификации в одном случае процессора, и
...