Typischer Projektfall 15 – Datenverwirrung aufgrund der Verwendung globaler Variablen in einer Umgebung mit hoher Parallelität Eine große Anzahl von Objekten wird in einer Umgebung mit hoher Parallelität erstellt, was zu einem Anstieg von GC und CPU führt

1: Einführung in den Hintergrund

  1. Es gibt einen unvollständigen Domänennamen, der mit globalen Variablen zusammenhängt.
    Bildbeschreibung hier einfügen
  2. Jede Anmeldung erstellt ein Objekt neu und legt es in eine öffentliche Variable. Wenn Sie auf hohe Parallelität stoßen, wird hier eine große Anzahl von Objekten erstellt, und dann verliert das vorherige Objekt seine Referenz und wartet darauf, dass der Müll von letzterem gesammelt wird, und häufiges GC lässt die CPU in die Höhe schnellen.
    Bildbeschreibung hier einfügen

Bildbeschreibung hier einfügen

Zweitens: Ideen & Lösungen

Bei Problem eins führt die Verwendung globaler Variablen in einer gleichzeitigen Umgebung gelegentlich zu Datenverwirrung.

Lösung 1: Ändern Sie die globale Variable in eine lokale Variable.

Lösung 2: Verwenden Sie ThreadLocal, die Thread-Variable ist von anderen Threads isoliert, und diese Variable ist für den aktuellen Thread eindeutig, sodass es kein Problem mit gemeinsam genutzten Thread-Variablen gibt.

Lösung 3: Verwenden Sie „synced“ für Methoden oder Klassen, die globale Variablen verwenden , und stellen Sie schließlich die globalen Variablen wieder her.

Lassen Sie uns das zweite und dritte Schema unten üben.Lassen Sie uns vor der Übung das Problem der Datenverwirrung simulieren, wenn globale Variablen unter gleichzeitigen Bedingungen verwendet werden.

Das Datenchaos taucht wieder auf

Geschäftsprozess: Starten Sie 100 Threads, um zwei Methoden für das Spleißen von Zeichenfolgen und dann für die Ergebnisse des Datenspleißens aufzurufen.
Berechnung

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public  void count2() throws InterruptedException {
    
    
        keyList = new ArrayList<>();
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);

       }

Client-Klasse

public class Client {
    
    
    public static void main(String[] args) {
    
    
        // 定义线程实现接口
        Runnable runnable = new Runnable(){
    
    
            Counter  counter = new Counter();

            @Override
            public void run() {
    
    
                try {
    
    
                    counter.count2();
                } catch (InterruptedException e) {
    
    
                    throw new RuntimeException(e);
                }
            }
        };
        // 启动100个线程
        for( int i= 0;i< 100;i++) {
    
    
            new Thread(runnable).start();
        }
    }

}

Ergebnisse erzielen
Datenverwirrung tritt auf
Bildbeschreibung hier einfügen

Verwenden Sie ThreadLocal



    private static ThreadLocal<List<String>> keyList = new ThreadLocal<List<String>>(){
    
    
        //对keyList进行初始化
        @Override public List<String> initialValue() {
    
    
            return new ArrayList<String>();
        }
    };
    public  void count2() throws InterruptedException {
    
    

        keyList.get().add("a");
        keyList.get().add("b");
        keyList.get().add("c");
        keyList.get().add("d");
        System.out.println("ARPRO"+keyList.get());

       }

Ergebnisse erzielen
Keine Datenverwirrung
Bildbeschreibung hier einfügen

Optimierung mit synchronisiert

Synchronized stellt sicher, dass nur ein Thread die Methode zu einem bestimmten Zeitpunkt ausführt, wodurch die Thread-Sicherheit gewährleistet wird
Synchronized kann Methoden, Objektinstanzen, bestimmte Klassen und Codeblöcke ändern

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public synchronized void count2() throws InterruptedException {
    
    
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
        //由于虽然使用synchronized锁住了count2()这个方法保证同一时刻只有一个线程执行
        //但是线程共享全局变量,所以当方法执行完成之后,需要将keyList的值进行还原
        keyList.removeAll(keyList);

       }

Der Unterschied zwischen ThreadLocal und synchronisiert

1. Synchronized wird für die gemeinsame Nutzung von Daten zwischen Threads verwendet, während ThreadLocal für die Datenisolierung zwischen Threads verwendet wird.

2. Synchronized ist ein Mechanismus, der Sperren verwendet, und auf einen Codeblock sollte jeweils nur von einem Thread zugegriffen werden. Und ThreadLocal stellt für jeden Thread eine Instanzkopie der Variablen bereit.

Bei Problem 2 wird jedes Mal, wenn Sie sich anmelden, ein Objekt neu erstellt und in eine öffentliche Variable gestellt. Wenn Sie auf hohe Parallelität stoßen, wird hier eine große Anzahl von Objekten erstellt, und dann verliert das vorherige Objekt seine Referenz und wartet darauf, dass der Müll von letzterem gesammelt wird, und häufiges GC lässt die CPU in die Höhe schnellen.

  1. Wenn wir ein Objekt instanziieren, wird das Objekt im Heap gespeichert und die Referenz auf dieses Objekt wird im Stack gespeichert. Wenn wir das keyList-Objekt erneut instanziieren, wird dieses Objekt erneut erstellt, und eine Referenz auf dieses Objekt wird auf dem Stapel neu erstellt.

  2. Warum führt häufiges GC zu einer hohen CPU-Auslastung?Die Anzahl der Threads, die sich die CPU-Zeitscheibe teilen, ist innerhalb eines bestimmten Zeitraums begrenzt.Je mehr Zeitscheiben von Threads belegt werden, die "nicht geschäftliche Arbeiten" ausführen, desto höher ist die CPU-Auslastungsrate.

  3. Häufige GC erhöhen die Threads „nicht geschäftlicher Arbeit". Diese Threads belegen eine gewisse Menge an CPU-Zeitfragmentierung, was zu einer Verringerung der CPU-Leerlaufzeit und einer Erhöhung der CPU-Auslastung führt.

Bildbeschreibung hier einfügen
Optimierung des Codes
Kombiniert mit der Optimierung von Problem 1 kann dieses Problem gelöst werden.

Zum Beispiel:

//声明一个全局变量,ArrayList线程不安全
    List<String> keyList=new ArrayList<>();
    public synchronized void count2() throws InterruptedException {
    
    
        keyList.add("a");
        keyList.add("b");
        keyList.add("c");
        keyList.add("d");
        System.out.println("ARPRO"+keyList);
        //由于虽然使用synchronized锁住了count2()这个方法保证同一时刻只有一个线程执行
        //但是线程共享全局变量,所以当方法执行完成之后,需要将keyList的值进行还原
        keyList.removeAll(keyList);

       }

Vier: Zusammenfassung

  1. Im Prozess der Projektentwicklung ist einerseits der Einsatz öffentlicher Variablen abzuwägen, ob Nebenläufigkeit und Multithreading vorhanden sind und dann die entsprechenden Verarbeitungsmaßnahmen der konkreten Situation entsprechend auszuwählen.
  2. Auch bei der Instanziierung von Klassen ist Vorsicht geboten: Klassen müssen instanziiert werden.

Guess you like

Origin blog.csdn.net/wangwei021933/article/details/129627351