• ,

task2707 - Определяем порядок захвата монитора

Реализуй логику метода isNormalLockOrder, который должен определять:
соответствует ли порядок synchronized блоков в методе someMethodWithSynchronizedBlocks — порядку передаваемых в него аргументов.
В случае, если сначала происходит синхронизация по o1, а потом по o2, метод должен вернуть true.
Если наоборот — false.

Требования:
1. Метод isNormalLockOrder должен возвращать true в случае, если синхронизация в методе someMethodWithSynchronizedBlocks происходит сначала по объекту o1, а потом по o2.
2. Метод isNormalLockOrder должен возвращать false в случае, если синхронизация в методе someMethodWithSynchronizedBlocks происходит сначала по объекту o2, а потом по o1.
3. Метод isNormalLockOrder НЕ должен быть приватным.
4. Класс Solution НЕ должен быть объявлен с модификатором final.

public class Solution {
    public void someMethodWithSynchronizedBlocks(Object obj1, Object obj2) {
        synchronized (obj2) {
            synchronized (obj1) {
                System.out.println(obj1 + " " + obj2);
            }
        }
    }

    public static boolean isNormalLockOrder(final Solution solution, final Object o1, final Object o2) throws Exception {

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                solution.someMethodWithSynchronizedBlocks(o1,o2);
            }
        };
        synchronized (o1) {
            thread1.start();
            while (true) {
                Thread.State ts = thread1.getState();
                if (ts == Thread.State.BLOCKED) {
                    System.out.println(thread1.holdsLock(o2));
                    break;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        final Solution solution = new Solution();
        final Object o1 = new Object();
        final Object o2 = new Object();

        System.out.println(isNormalLockOrder(solution, o1, o2));
    }
}


Почему thread1.holdsLock(o2) — при любом раскладе, выдает false ???
и как получить .holdsLock(o2) держателя мютекса у объекта о2 ???

7 комментариев

Peras
по всей видимости, это происходит из-за того, что ты выполняешь проверку holdsLock в блоке, который синхронизирован по о1, тогда как метод someMethodWithSynchronizedBlocks уже отработал и разлочил о2
WeLLus
Не складывается у меня…
С запуском программы, по умолчанию появляется Главная нить main.
1. Она выполняет метод main(), где создает создает 3 объекта solution, o1 и o2
2. Затем заходит в isNormalLockOrder(solution, o1, o2) со ссылками на эти объекты
3. Далее она заходит в synchronized блок, захватывает мютекс объекта о1 и стартует другую нить thread1.
С этого момента объект о1 залочен.
4. thread1 запускает свой run(), в котором у объекта solution вызывает метод someMethodWithSynchronizedBlocks(o1,o2).
5. Теперь thread1 заходит в someMethodWithSynchronizedBlocks и пытается залочить объект о2.
По моему пониманию, у thread1 должно получиться залочить о2, ведь нить main держит только о1, а уж потом попытается залочить о1 и уйтдет в BLOCKED, дожидаться освобождения о1, но при этом занимая о2…

Вот я так чувствую, что у меня неправильное понимание п.4
Так как я ставил:
System.out.println(thread1.getName() + " lock(o1) = " + thread1.holdsLock(o1) + "; lock(o2) = " + thread1.holdsLock(o2));
System.out.println(Thread.currentThread().getName() + " lock(o1) = " + Thread.currentThread().holdsLock(o1) + "; lock(o2) = " + Thread.currentThread().holdsLock(o2));

и вывод показал следующее:
Thread-0 lock(o1) = true; lock(o2) = false
main lock(o1) = true; lock(o2) = false


т.е. main и Thread-0 — это одна и та же нить???
Помогите разобраться…
Peras
Thread thread1 = new Thread(){
            @Override
            public void run() {
                solution.someMethodWithSynchronizedBlocks(o1,o2);
            }
        };
        synchronized (o1) {   //лочим о1
            thread1.start();  //лочим о1 и о2
//  метод someMethodWithSynchronizedBlocks завершился, лок с о2 снялся
            while (true) {
                Thread.State ts = thread1.getState();
                if (ts == Thread.State.BLOCKED) {
                    System.out.println(thread1.holdsLock(o2)); // а здесь мы проверяем на о2 лок 
                    break;
                }
            }

Я думаю, что в этом проблема
WeLLus
Но ведь в методе someMethodWithSynchronizedBlocks сначала лочится о2…
и для thread1 этот метод не может завершиться пока залочен о1.

public void someMethodWithSynchronizedBlocks(Object obj1, Object obj2) {
        synchronized (obj2) { //сначала лочится о2
            synchronized (obj1) { // а здесь thread1 должен уйти в состояние BLOCKED, так как о1 залочен нитью main.
                System.out.println(obj1 + " " + obj2);
            }
        }
    }


и вот тут я не пойму, почему
synchronized (o1) {   //лочим о1
            thread1.start();  //лочим о1 и о2
////  метод someMethodWithSynchronizedBlocks завершился, лок с о2 снялся
// но ведь метод someMethodWithSynchronizedBlocks для нити thread1 не может завершиться, так как о1 залочен здесь нитью main
            while (true) {
                Thread.State ts = thread1.getState();
                if (ts == Thread.State.BLOCKED) {
// то, что someMethodWithSynchronizedBlocks не выполнился, подтверждается тем, что нить main проверяет статус thread1 и получает BLOCKED
                    System.out.println(thread1.getName() + " lock(o1) = " + thread1.holdsLock(o1) + "; lock(o2) = " + thread1.holdsLock(o2));
                    System.out.println(Thread.currentThread().getName() + " lock(o1) = " + Thread.currentThread().holdsLock(o1) + "; lock(o2) = " + Thread.currentThread().holdsLock(o2));
                    break;
                }
            }

вот вывод

Thread-0 lock(o1) = true; lock(o2) = false
main lock(o1) = true; lock(o2) = false


Я думаю, что подсказка в этом выводе. Для обеих нитей ЛОК на о1 — а такого быть не может…
Peras
  • Peras
  • 0
  • Комментарий отредактирован 2017-11-03 17:01:59 пользователем Peras
Взял твой код, поменял выводы на консоль и вот что увидел:
<code>
public class Solution {
    public void someMethodWithSynchronizedBlocks(final Object obj1, final Object obj2) {
        synchronized (obj2) {
        	System.out.println("entered sync o2");
            synchronized (obj1) {
                System.out.println(obj1 + " " + obj2);
            }
        }
    }

    public static boolean isNormalLockOrder(final Solution solution, final Object o1, final Object o2) throws Exception {

    	Thread main = Thread.currentThread();
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                solution.someMethodWithSynchronizedBlocks(o1,o2);
            }
        };
        synchronized (o1) {
        	thread1.start();
            while (true) {
                Thread.State ts = thread1.getState();
                if (ts == Thread.State.BLOCKED) {
                	System.out.println(main.holdsLock(o1));
                    System.out.println(thread1.holdsLock(o1));
                    break;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        final Solution solution = new Solution();
        final Object o1 = new Object();
        final Object o2 = new Object();

        System.out.println(isNormalLockOrder(solution, o1, o2));
    }
}
</code>
Консоль:
<code>entered sync o2
true
true
false</code>
java.lang.Object@6c6c741 java.lang.Object@80139e9
примерно в этой точке:
<code>};
        synchronized (o1) {
        	thread1.start();</code>
Оба потока повисли на объект о1.
WeLLus
Так я вот этого понять и не могу.
По теории, на одном объекте может только одна нить висеть… а чего они оба на этом объекте делают???
WeLLus
В общем решил я ее…
А holdsLock(), по всей видимости, показывает локи на объекты из которой этот holdsLock() вызывается…
и не важно какую нить я ему буду указывать мониторить — thread1.holdsLock(o1), thread2.holdsLock(o1)… threadN.holdsLock(o1).
Для всех этих запросов он покажет результат для Thread.currentThread().holdsLock(o1)…
Что и ввело меня в заблуждение.

а решение задачи такое:

package com.javarush.task.task27.task2707;

/*
Определяем порядок захвата монитора
*/
public class Solution {
    public void someMethodWithSynchronizedBlocks(Object obj1, Object obj2) {
        synchronized (obj1) {
            synchronized (obj2) {
                System.out.println(obj1 + " " + obj2);
            }
        }
    }

    public static boolean isNormalLockOrder(final Solution solution, final Object o1, final Object o2) throws Exception {

        Thread thread1 = new Thread(){
            @Override
            public void run() {
                solution.someMethodWithSynchronizedBlocks(o1,o2); // 3. Пытаемся выполнить метод someMethodWithSynchronizedBlocks
            }
        };

        Thread thread2 = new Thread() {
            @Override
            public void run() {
                synchronized (o2) { // 6. Если у этой нити получилось заблочить о2, значит порядок в методе someMethodWithSynchronizedBlocks - ВЕРНЫЙ
                    Thread.currentThread().interrupt();
                }
            }
        };

        synchronized (o1) {  // 1. Лочим о1
            thread1.start(); // 2. Запускаем thread1
            while (true) {
                if (thread1.getState() == Thread.State.BLOCKED) { // 4. Ждем, когда thread1 заблочится и выходим из while
                    break;
                }
            }

            thread2.start(); // 5. Запускаем thread2
            while (!thread2.isInterrupted()) { // 7. Пока нить thread2 работает, проверяем ее состояние
                Thread.State ts = thread2.getState();
                if (ts != Thread.State.RUNNABLE) { // 8. Нас интересует любое состояние, кроме RUNNABLE
                    if (ts == Thread.State.BLOCKED) { // 9. Отловили BLOCKED у thread2, значит заблокированны оба
                                                    // обекта о1 и о2 и порядок блокировки в методе
                                                    // someMethodWithSynchronizedBlocks - НЕ ВЕРНЫЙ
                        return false;
                    } else {                        // 10. Если нить thread2 сменила свое состояние с RUNNABLE на
                                                    // любое другое кроме BLOCKED (в нашем случае, скорее всего
                                                    // TERMINATED), значит объект о2 на заблокирован. Следовательно,
                                                    // порядок блокировки в методе someMethodWithSynchronizedBlocks - ВЕРНЫЙ
                        return true;
                    }
                }
            }
        }

        return true;
    }

    public static void main(String[] args) throws Exception {
        final Solution solution = new Solution();
        final Object o1 = new Object();
        final Object o2 = new Object();

        System.out.println(isNormalLockOrder(solution, o1, o2));
    }
}
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.