web-dev-qa-db-fra.com

Si une méthode synchronisée appelle une autre méthode non synchronisée, y a-t-il un verrou sur la méthode non synchronisée

En Java, si une méthode synchronisée contient un appel à une méthode non synchronisée, une autre méthode peut-elle toujours accéder à la méthode non synchronisée en même temps? Fondamentalement, ce que je demande, c'est que tout dans la méthode synchronisée est verrouillé (y compris les appels à d'autres méthodes synchronisées)? Merci beaucoup

42
dido

Si une méthode synchronisée appelle une autre méthode non synchronisée, y a-t-il un verrou sur la méthode non synchronisée

Oui et non.

Si vous êtes dans une méthode synchronized, les appels à d'autres méthodes qui sont également synchronized par d'autres threads sont verrouillés. Cependant, les appels à des méthodes non synchronisées par d'autres threads sont pas verrouillés - n'importe qui peut les appeler en même temps.

public synchronized void someSynchronizedMethod() {
    ...
    someNonSynchronizedMethod();
    ...
}

// anyone can call this method even if the someSynchronizedMethod() method has
// been called and the lock has been locked
public void someNonSynchronizedMethod() {
   ...
}

De plus, si vous appelez someSynchronizedMethod() mais que vous vous trouvez dans la méthode someNonSynchronizedMethod(), vous maintenez toujours le verrou. Le verrouillage est activé lorsque vous entrez dans un bloc synchronisé et est désactivé lorsque vous quittez cette méthode. Vous pouvez appeler toutes sortes d'autres méthodes non synchronisées et elles seront toujours verrouillées.

Mais vous posez deux choses différentes dans votre question:

En Java, si une méthode synchronisée contient un appel à une méthode non synchronisée, une autre méthode peut-elle toujours accéder à la méthode non synchronisée en même temps?

Oui. D'autres méthodes peuvent accéder aux méthodes non synchronisées.

Fondamentalement, ce que je demande, c'est que tout dans la méthode synchronisée est verrouillé (y compris les appels à d'autres méthodes synchronisées)?

Euh, oui. Les autres appels aux méthodes synchronized sont verrouillés. Mais les méthodes non synchronisées ne sont pas verrouillées.

N'oubliez pas non plus que si la méthode est static, le verrou se trouve sur l'objet Class dans le ClassLoader.

// this locks on the Class object in the ClassLoader
public static synchronized void someStaticMethod() {

Si la méthode est une méthode d'instance, le verrou se trouve sur l'instance de la classe.

// this locks on the instance object that contains the method
public synchronized void someInstanceMethod() {

Il y a 2 verrous différents dans ces 2 cas.

Enfin, lorsque vous traitez avec des méthodes d'instance synchronized, chaque instance de la classe est ce qui est verrouillé. Cela signifie que deux threads peuvent être dans la même méthode synchronized en même temps avec différents instances. Mais si 2 threads tentent d'opérer sur les méthodes synchronized sur la même instance, l'un se bloquera jusqu'à ce que l'autre quitte la méthode.

Si le thread A appelle la méthode synchronisée M1 qui à son tour appelle la méthode non synchronisée M2, le thread B peut toujours appeler M2 sans blocage.

La méthode synchronisée acquiert et libère un verrou intrinsèque sur l'objet sur lequel elle est appelée. C'est pourquoi il peut se bloquer. La méthode non synchronisée n'essaie pas d'acquérir un verrou (sauf si cela est fait explicitement dans le code).

Ainsi, si vous devez également assurer l'exclusion mutuelle pour M2, vous devez le synchroniser, que ses appelants (comme M1) soient synchronisés ou non.

3
Adam Zalcman

Le verrou n'appartient pas au fil. Le verrou appartient en fait à l'objet (ou à la classe en cas de verrouillage de niveau classe), et un thread acquiert un verrou sur l'objet (ou la classe en cas de verrouillage de niveau classe) dans un contexte synchronisé. Maintenant, il n'y a pas de propagation de verrouillage dans Java comme cela est discuté ci-dessus. Voici une petite démo:

public class TestThread {

    /**
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ThreadCreator1 threadCreator1 = new ThreadCreator1();
        ThreadCreator2 threadCreator2 = new ThreadCreator2();

        Thread t1 = new Thread(threadCreator1,"Thread 1");
        Thread t3 = new Thread(threadCreator1,"Thread 3");
        Thread t2 = new Thread(threadCreator2,"Thread 2");

        t1.start();
        Thread.sleep(2000);
        t3.start();

    }
}
public class ThreadCreator1 implements Runnable {

    private static final Task task= new Task();
    private static final Task2 task2= new Task2();

    @Override

    public void run() {

        try {

            if(Thread.currentThread().getName().equals("Thread 1"))
                task.startTask2(task2);
            if(Thread.currentThread().getName().equals("Thread 3"))
                task2.startTask();

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // TODO Auto-generated method stub

        /**/

        }
    }

public class Task {

    public static final Task task = new Task();
    public static List<String> dataList = new ArrayList<String>();
    ReentrantLock lock =  new ReentrantLock();



    public  void startTask2(Task2 task2) throws InterruptedException
    {

        try{

            lock.lock();
            //new Task2().startTask();
            task2.startTask();
        }
        catch(Exception e)
        {

        }
        finally{
            lock.unlock();
        }
    }

}
public class Task2 {

    ReentrantLock lock = new ReentrantLock();
    public  void startTask() throws InterruptedException
    {

        try{
            //lock.lock();
            for(int i =0 ;i< 10;i++)
        {
            System.out.println(" *** Printing i:"+i+" for:"+Thread.currentThread().getName());
            Thread.sleep(1000);
        }
        }
        catch(Exception e)
        {

        }
        /*finally
        {
            lock.unlock();
        }*/
    }

}

J'ai juste utilisé le verrou Reentrant ici. Si le code ci-dessus est exécuté, il y aura entrelacement entre le thread 1 et le thread 3, mais si la partie de verrouillage de la classe Task2 n'est pas commentée, il n'y aura pas d'entrelacement et le thread qui acquiert le verrou en premier se terminera entièrement en premier, puis il libérera le verrou et l'autre fil pourra continuer.

2
user2398545

Le verrou appartient au thread, pas à la méthode (ou plus précisément, son cadre de pile). Il se trouve que si vous avez une méthode synchronisée, vous êtes assuré que le thread possédera le verrou avant le début du corps de la méthode et le relâchera ensuite.

Un autre thread peut toujours invoquer la deuxième méthode non synchronisée. Une méthode non synchronisée peut être appelée par n'importe quel thread à tout moment.

0
yshavit