Maison  >  Questions et réponses  >  le corps du texte

concurrence java sans verrouillage

Le ratio sans verrouillage et verrouillé dans le code suivant est-il une meilleure implémentation ? J'utilise jmeter pour effectuer 20 requêtes par seconde. La majeure partie de la sortie de l'opération de veille dans l'exécution du code sans verrouillage test() est extrêmement différente de 500 millisecondes, tandis que la sortie du code verrouillé est essentiellement de 500 millisecondes avec une différence de 1 ou 2 millisecondes. Ce problème est très étrange....

@Controller
@RequestMapping("/bench/")
public class BenchController {

    @Autowired
    private FlowService flowService;

    private static Object[] lockObj;
    private static AtomicReference<Integer>[] locks;

    static {
        lockObj = new Object[100];
        for (int i = 0; i < lockObj.length; i++) {
            lockObj[i] = new Object();
        }

        locks = new AtomicReference[100];
        for (int i = 0; i < locks.length; i++) {
            locks[i] = new AtomicReference<Integer>(null);
        }
    }

    @RequestMapping("a")
    @ResponseBody
    public long a(int id) throws Exception {
        long start = System.currentTimeMillis();
        int index = id % 100;
        long inner=0;
        synchronized (lockObj[index]) {
            inner=test();
        }
        long result = System.currentTimeMillis() - start;
        System.out.println("all: "+result+" inner: "+inner);
        return result;
    }

    @RequestMapping("b")
    @ResponseBody
    public long b(int id) throws Exception {
        long start = System.currentTimeMillis();
        AtomicReference<Integer> lock=locks[id % 100];
        while (!lock.compareAndSet(null, id)) {}
        long inner=test();
        boolean flag=lock.compareAndSet(id, null);
        long result = System.currentTimeMillis() - start;
        System.out.println("all: "+result+" inner: "+inner+" flag:"+flag);
        return result;
    }

    public long test()throws Exception{
        long innerstart = System.currentTimeMillis();
        Thread.sleep(500);
        System.out.println(System.currentTimeMillis()-innerstart);
        return System.currentTimeMillis()-innerstart;
    }
}
我想大声告诉你我想大声告诉你2713 Il y a quelques jours693

répondre à tous(1)je répondrai

  • 曾经蜡笔没有小新

    曾经蜡笔没有小新2017-05-17 10:03:02

    1. Tout d'abord, clarifions deux problèmes. Synchronized n'est généralement pas comparé à la classe AtomicXX, mais plutôt à la classe ReentrantLock. Il existe de nombreuses comparaisons entre les deux sur Internet, vous pouvez les rechercher vous-même.

    2. Concernant la question de l'absence de serrure ou de verrouillage, le code du code test b pose problème,

    • Pour la méthode a, bloc de code synchronisé, une fois le verrou détenu par le premier thread entrant, les threads suivants demandant l'acquisition du verrou seront bloqués et suspendus. Jusqu'à ce que le thread précédent libère le verrou, les threads suivants reprendront l'exécution. à l'existence du verrou, 20 requêtes sont similaires à une exécution séquentielle, cette couche est planifiée par jvm

    • Pour la méthode b, l'opération cas n'est pas bloquante. La boucle while dans la méthode est en fait toujours en cours d'exécution (essayant continuellement d'effectuer une opération cas), et nous savons que la boucle infinie consommera des ressources CPU. Plus il y a de threads, plus il y a d'opérations CAS ici, ce qui entraînera inévitablement une augmentation de l'utilisation du processeur. Lorsque le code de la méthode B est testé par jmeter, il devrait théoriquement toujours y avoir 20 threads de travail actifs. Le modèle de processeur et de thread est un autre sujet. , le réglage du numéro de fil est un sujet relativement avancé dans jvm. Si vous êtes intéressé, vous pouvez le rechercher vous-même sur Google

    • .
    • Parlons de ReentrantLock et synchronisé : généralement dans des conditions de concurrence élevée, ReentrantLock a de meilleures performances que synchronisé, et ReentrantLock fournit certaines fonctions que synchronisées ne fournissent pas (abandon automatique du délai d'expiration du verrouillage, etc.). temps, simulant ainsi des pauses plus courtes et une concurrence plus élevée. 500 ms est très court pour les humains, mais c'est fondamentalement un nombre astronomique pour les processeurs. Fondamentalement, il n'est pas exagéré de le décrire comme "lent comme un escargot". un exemple. Code :

    package com.gzs.learn.springboot;
    
    import java.util.LinkedList;
    import java.util.Random;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    import java.util.concurrent.locks.ReentrantLock;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    @RequestMapping("/bench/")
    public class BenchController {
        private Random random = new Random();
        private static Object[] lockObj;
        private static AtomicReference<Integer>[] locks;
        private static ReentrantLock[] reentrantLocks;
        static {
            lockObj = new Object[100];
            for (int i = 0; i < lockObj.length; i++) {
                lockObj[i] = new Object();
            }
    
            locks = new AtomicReference[100];
            for (int i = 0; i < locks.length; i++) {
                locks[i] = new AtomicReference<Integer>(null);
            }
            reentrantLocks = new ReentrantLock[100];
            for (int i = 0; i < reentrantLocks.length; i++) {
                reentrantLocks[i] = new ReentrantLock();
            }
        }
    
        @RequestMapping("a/{id}")
        @ResponseBody
        public long a(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            int index = id % 100;
            long inner = 0;
            synchronized (lockObj[index]) {
                inner = test();
            }
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner);
            return result;
        }
    
        @RequestMapping("b/{id}")
        @ResponseBody
        public long b(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            id = id % 100;
            AtomicReference<Integer> lock = locks[id];
            int b = 0;
            while (!lock.compareAndSet(null, id)) {
                b = 1 + 1;
            }
            long inner = test();
            boolean flag = lock.compareAndSet(id, null);
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner + " flag:" + flag);
            System.out.println(b);
            return result;
        }
    
        @RequestMapping("c/{id}")
        @ResponseBody
        public long c(@PathVariable("id") int id) throws Exception {
            long start = System.currentTimeMillis();
            id = id % 100;
            ReentrantLock lock = reentrantLocks[id];
            lock.lock();
            long inner = test();
            lock.unlock();
            long result = System.currentTimeMillis() - start;
            System.out.println("all: " + result + " inner: " + inner);
            return result;
        }
    
        public long test() throws Exception {
            long innerstart = System.currentTimeMillis();
            Thread.sleep(0, 100);
            // Thread.sleep(500);
            System.out.println(System.currentTimeMillis() - innerstart);
            return System.currentTimeMillis() - innerstart;
        }
    }
    
    • La méthode c est implémentée à l'aide de ReentrantLock Dans la plupart des cas, ReentrantLock est plus efficace que synchronisé

    • . La classe principale Aqs (AbstractQueuedSynchronizer) dans
    • juc (java.util.concurrent) est un package de concurrence basé sur une file d'attente. Par défaut, le thread sera garé (opération suspendue) lorsque la concurrence de verrouillage (spin) dépasse 1 000 nanosecondes. Afin de réduire les changements de thread fréquents du processeur, vous pouvez essayer d'ajuster le paramètre de temps de veille dans la méthode c.

    • Méthode de test, jmeter n'est pas installé sur cette machine, le test est effectué avec apache ab, commande test :

    ab -n 100 -c 20  http://localhost:8080/bench/a/10

    répondre
    0
  • Annulerrépondre