Если призван метод synchronized: только трэд, который это призывает, имеет доступ к инстанции Объекта?

У меня есть инстанция Объекта и я звоню в метод synchronized этой инстанции с трэда.

Пока работает этот метод: другие трэды могут соглашаться на эту же самую инстанцию?

1
задан 12.01.2017, 23:24
0 ответов

Простой и прямой ответ - что, выполнять метод synchronized не состоит в себе гарантия в том, чтобы другие трэды не смогли соглашаться на членов или выполнять методы объекта.

Более подробное объяснение

Ты гарантировал, выполнив метод synchronized:

  • Пока работал метод synchronized инстанции, названный M, никакой другой трэд не может выполнять:
    1. код, который был бы "защищен" блоком synchronized(obj) { ... } [1] где obj это ссылка на ту же инстанцию, которой принадлежит метод M.
    2. тот же метод M или любой другой метод synchronized инстанции, которая принадлежала бы той же инстанции.
  • Пока работал метод synchronized класса или статический названный M, никакой другой трэд не может выполнять:
    1. код, который был бы "защищен" блоком synchronized(A.class) { ... } [1] где A это класс, который определяет метод M.
    2. тот же метод M или любой другой метод synchronized класса или статический, что принадлежал тому же классу A.

Или, обобщая вышесказанное, пока ты выполнил какой-то код, "защищенный" блоком synchronized(obj1) { ... } [1] [2], никакой другой трэд не может выполнять тот же код или любой другой код, защищенный блоком synchronized(obj2) { ... } [2] где obj1 и obj2 это ссылки на тот же объект.

Направляя в вопрос, это signfica, что пока ты выполнил метод synchronized для инстанции x, ничего нет, что он помешал тому, чтобы другие трэды согласились на членов или выполнили методы x что не были защищены блоком synchronized(x) [1] [2].

Демонстрации

Например, здесь у нас есть код с 2 трэдами, что оба стараются выполнять одновременно методы synchronized отличные от той же инстанции:

class Rextester
{  
    public static void main(String args[]) throws Exception {
        Compartido compartido = new Compartido();
        new Thread(() -> compartido.m1()).start();
        Thread.sleep(10);
        new Thread(() -> compartido.m2()).start();
    }

    private static class Compartido {
        public synchronized void m1() {
            logLlamada("m1");
        }

        public synchronized void m2() {
            logLlamada("m2");
        }

        private void logLlamada(String methodName) {
            System.out.printf("%s - entrada%n", methodName);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.printf("%s - salida%n", methodName);
        }
    }
}

Благодаря тому, что 2 метода m1 и m2 они synchronized и они той же инстанции, второй трэд ждет, что выполнение m1 закончитесь перед тем, как выполнять m2, поскольку это показывает результат:

m1 - entrada
m1 - salida
m2 - entrada
m2 - salida

Demo

В контрасте, если мы изменяем код и лишаем его модификатора synchronized в метод m2:

class Rextester
{  
    public static void main(String args[]) throws Exception {
        Compartido compartido = new Compartido();
        new Thread(() -> compartido.m1()).start();
        Thread.sleep(10);
        new Thread(() -> compartido.m2()).start();
    }

    private static class Compartido {
        public synchronized void m1() {
            logLlamada("m1");
        }

        public void m2() { // no synchronized
            logLlamada("m2");
        }

        private void logLlamada(String methodName) {
            System.out.printf("%s - entrada%n", methodName);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.printf("%s - salida%n", methodName);
        }
    }
}

... тогда мы замечаем, что, хотя m1 он synchronized, это не мешает тому, чтобы второй трэд смог выполнять m2 одновременно, поскольку это показывает результат:

m1 - entrada
m2 - entrada
m1 - salida
m2 - salida

Demo


[1] Когда я использую выражение код, "защищенный" блоком synchronized(obj) { ... }, я отношусь, у которого код логически (в батарейке вызовов) остается внутри блока synchronized(obj) { ... } в момент того, чтобы работать.

[2] Метод synchronized инстанции он эквивалентный "нормальному" методу инстанции с блоком synchronized(this) { ... }. И метод synchronized статический он эквивалентный "нормальному" статическому методу с блоком synchronized(A.class) { ... } где A это класс, который определяет статический метод.

4
ответ дан 03.12.2019, 17:59
  • 1
    Тема - má s осложненный того, что похожим на простой вид. Следующее неправильное: " Пока работал mé каждый synchronized инстанции так называемый M, ningú n другой трэд может выполнять: có я говорю, что esté окруженный блоком synchronized (obj) {...}, где obj - ссылка на ту же инстанцию, которой принадлежит mé весь M." –  13.01.2017, 10:42
  • 2
    Привет José. Верно, что это сложная тема. Поэтому traté того, чтобы быть очень заботливым с té rminos, что usé. ¿ Ты можешь объяснять то, что тебе кажется неправильным с этим решением? –  13.01.2017, 13:40
  • 3
    Я существую añ adido в мой ответ пример, который противоречит этому решению. –  13.01.2017, 14:34
  • 4
    Jeje, договора. " rodeado" это не был té rmino приведенный в соответствие. Ло cambié из-за " protegido" так как, как demostrastes, возможно " rodear" có я говорю внутри блока synchronized без того, чтобы этот có я говорю, что он принадлежал ему ló gicamente в этот блок. –  13.01.2017, 15:21

ДА, могут соглашаться другие трэды.

Трэд может соглашаться на Объект с независимостью, которой другой трэд выполнил бы метод synchronized этого объекта.

Следующая программа это показывает:

public class SynchronizedTest {
    static volatile boolean bloqueado = false;
    static final ObjetoBloqueado objBloqueado = new ObjetoBloqueado();

    public static void main(String[] args) {
        // Crear un objeto Bloqueador.
        Bloqueador bloqueador = new Bloqueador();
        // Lanzarlo
        bloqueador.start();
        // Esperar a que el thread bloqueador haya llamado al 
        //método synchronized
        while( !bloqueado )
            Thread.yield();
        // Modificar el objeto bloqueado.
        objBloqueado.n = 20;
        // Imprimirlo para ver que si hemos podido modificarlo pese a estar
        //bloqueado
        System.out.println( objBloqueado.n );

        System.exit(0);
    }

    static class ObjetoBloqueado
    {
        public volatile int n;
        public synchronized void bloquea()
        {
            // Asignar un valor en el objeto bloqueado.
            n = 10;
            // Marcar que el objeto está bloqueado.
            SynchronizedTest.bloqueado = true;
            while(true)
                Thread.yield();
        }
    }

    static class Bloqueador extends Thread {
        @Override
        public void run()
        {
            SynchronizedTest.objBloqueado.bloquea();
        }
    }
}

В этой программе давайте верить в трэд класса Bloqueador который звонит в метод bloquea, что является методом synchronized объекта Synchronized.objBloqueado. Этот метод не заканчивается никогда.

Пока главный thread надеется на то, что работал этот метод synchronized. И после попробуй соглашаться на объект SynchronizedTest.objBloqueado, что может делать без проблем. Даже смоги изменять это!

Еще больше. Пока работал метод synchronized инстанции так называемо M, какой-то другой трэд да может выполнять код, который был бы окружен блоком synchronized (obj) {...}, где obj - ссылка на ту же инстанцию, которой принадлежит метод M.

Следующая программа это показывает:

class SynchronizedTest 
{  
    public static void main(String args[]) throws Exception {
        Compartido compartido = new Compartido();
        Aplicador ap;
        synchronized (compartido) {
            ap = new Aplicador() {
                public void func()
                {
                    compartido.logLlamada("func");
                }
            };
        }
        new Thread(() -> compartido.m1()).start();
        Thread.sleep(10);
        new Thread(() -> compartido.m2()).start();
        Thread.sleep(10);
        ap.func();
    }

    private static class Compartido {
        public synchronized void m1() {
            logLlamada("m1");
        }

        public synchronized void m2() {
            logLlamada("m2");
        }

        private void logLlamada(String methodName) {
            System.out.printf("%s - entrada%n", methodName);
            try {
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.printf("%s - salida%n", methodName);
        }
    }

    public interface Aplicador {
        void func();
    }
}

Код func будь окружен блоком synchronized(compartido) где compartido это ссылка на ту же инстанцию, которой принадлежат методы m1 и m2. Несмотря на это, func возможно выполнять одновременно в m1 и(или) m2. Вывод:

m1 - entrada
func - entrada
m1 - salida
m2 - entrada
func - salida
m2 - salida

Не достаточно, чтобы код был окружен блоком synchronized. Все же может быть внутри код, в предыдущем примере это код анонимного класса, определенного внутри этого блока, который не подчинен в synchronized что окружает его.

4
ответ дан 03.12.2019, 17:59

Теги

Похожие вопросы