многопоточная блокировка получения java не работает

#java #multithreading #locking #wait #notify

#java #многопоточность #блокировка #подождите #уведомлять

Вопрос:

Я пытаюсь написать небольшой фрагмент кода для блокировки и разблокировки блока кода. функции acquire_lock и release_lock такие, как показано ниже:

     public static void acquire_lock(long timestamp) {
    synchronized(operations) {
        // put the timestamp into queue
        operations.add(timestamp);
        // check if the head of queue is current timestamp, if not,
        // this means there are some other operations ahead of current one
        // so current operation has to wait
        while (operations.peek() != timestamp) {
            try {
                operations.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void release_lock() {
    synchronized(operations) {
        // poll the finished operation out of queue
        // and wake up all waiting operations
        operations.poll();
        operations.notifyAll();
    }
}
 

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

 public class AcquireLockNotWork {

static int balance = 0;
static PriorityQueue<Long> operations = new PriorityQueue<Long>();

// withdraw money from balance
public static void withdraw(final int amt) {
    // get system time
    Long timestamp = System.nanoTime();
    Thread t = new Thread(new Runnable() {
        public void run() {
            // try to use acquire_lock to lock this piece of code
            acquire_lock(timestamp);
            try {       
                Thread.sleep(500);
                int holdings = balance;
                balance = holdings - amt;
                System.out.println("Withdrew "   amt   " from funds. Now at "   balance);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                release_lock();
            }       
        }
    });
    t.start();
}

//put money into banlance
public static void deposit(int amt) {
    Thread t1 = new Thread(new Runnable() {
        public void run() {
            Long timestamp = System.nanoTime();
            acquire_lock(timestamp);
            int holdings = balance;
            balance = holdings   amt;
            System.out.println("deposit "   amt   ", balance: "   balance);
            release_lock();
        }
    });
    t1.start();
}

public static void acquire_lock(long timestamp) {
    synchronized(operations) {
        // put the timestamp into queue
        operations.add(timestamp);
        // check if the head of queue is current timestamp, if not,
        // this means there are some other operations ahead of current one
        // so current operation has to wait
        while (operations.peek() != timestamp) {
            try {
                operations.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void release_lock() {
    synchronized(operations) {
        // poll the finished operation out of queue
        // and wake up all waiting operations
        operations.poll();
        operations.notifyAll();
    }
}

public static void test1() {
    balance = 0;
    deposit(2000);
    withdraw(500);
    withdraw(1000);
}

public static void main(String[] args) {
    test1();
}
}
 

в течение небольшого количества раз результат будет таким:

 deposit 2000, balance: 2000
Withdrew 500 from funds. Now at 500
Withdrew 1000 from funds. Now at 500
 

это означает, что функции acquire_lock и release_lock не работают должным образом. Кажется, что последние два потока (отозвали 500 и отозвали 1000) вошли в блок между acquire_lock() и release_lock() одновременно, и это не то, что я хочу.
Итак, что не так с функциями acquire_lock и release_lock?

Комментарии:

1. Ваш код, похоже, полагается на System.nanoTime() то, что вы никогда не возвращаете одно и то же значение более одного раза, но такое поведение не гарантируется. Javadoc for System.nanoTime() говорит: «Этот метод обеспечивает наносекундную точность, но не обязательно наносекундное разрешение (то есть частоту изменения значения)»

Ответ №1:

Здесь это очень сложно. Аномальное происходит потому, что последний поток сначала вводит acquire_lock. И когда предыдущий поток переходит в acquire_lock, он не будет заблокирован, потому что код блокирует потоки на основе их метки времени. Таким образом, два потока переходят в одну и ту же защищенную область кода.