Heim  >  Artikel  >  Java  >  Java-Multithreading: Verwendung der Thread-Klasse.

Java-Multithreading: Verwendung der Thread-Klasse.

王林
王林nach vorne
2023-04-25 14:52:071298Durchsuche

    Grundlegende Verwendung der Thread-Klasse

    1. Erstellen Sie eine Unterklasse, erben Sie von Thread und überschreiben Sie die Ausführungsmethode :

    class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("hello thread");
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            // 最基本的创建线程的办法.
            Thread t = new MyThread();
            //调用了start方法才是真正的在系统中创建了线程,执行run方法
            t.start();
        }
    }

    2. Erstellen Sie eine Klasse, implementieren Sie die Runnable-Schnittstelle, erstellen Sie dann eine Runnable-Instanz und übergeben Sie sie an Thread

    class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println("hello");
        }
    }
    public class Demo3 {
        public static void main(String[] args) {
            Thread t = new Thread(new MyRunnable());
            t.start();
        }
    }
    #🎜 🎜## 🎜🎜#3. Anonyme innere Klasse

    Erstellen Sie eine anonyme innere Klasse, erben Sie von der Thread-Klasse, überschreiben Sie die Ausführungsmethode und erstellen Sie dann eine Instanz der anonymen inneren Klasse

    # ?? 4.Lambda-Ausdruck Lambda ersetzt Runnable

    public class Demo4 {
        public static void main(String[] args) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    System.out.println("hello");
                }
            };
            t.start();
        }
    }
    Thread-Indikator

    1.isDaemon();
    # 🎜 🎜#Ob der Hintergrundthread einen Einfluss auf den Prozessausgang hat, hat kein Hintergrundthread Auswirkungen auf den Prozessausgang

    2.isAlive();

    Ob er aktiv ist Während des Aufrufs gibt es vor dem Start keinen entsprechenden Thread. Der Thread wird nach der Ausführung der Ausführungsmethode zerstört. #Ob es unterbrochen ist
    #Das Unterschied zwischen run und start

    run ist einfach eine gewöhnliche Methode, die den Inhalt der Aufgabe beschreibt. start ist eine spezielle Methode, ein Thread wird intern im System erstellt

    Thread unterbrechen
    Der Schlüssel zum Stoppen des Threads besteht darin, die Ausführung der entsprechenden Ausführungsmethode zu beenden. Für den Hauptthread wird die Hauptmethode nicht beendet Bis die Hauptmethode ausgeführt wird, kann sich dieses Flag auf das Ende dieses Threads auswirken, aber mehrere Threads teilen sich hier einen virtuellen Raum, sodass der vom Hauptthread geänderte isQuit und der vom t-Thread beurteilte isQuit denselben Wert haben

    public class Demo5 {
        public static void main(String[] args) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello");
                }
            });
            t.start();
        }
    }
    #🎜🎜 #2. Verwenden Sie ein in Thread integriertes Flag, um
    Thread.interruted() zu bestimmen. Dies ist eine statische Methode Thread.currentThread().isInterrupted () Dies ist eine Instanzmethode. Unter anderem kann currentThread die Instanz des aktuellen Threads abrufen .interrupt() kann zwei Situationen erzeugen: # 🎜🎜#

    1) Wenn der T-Thread bereit ist, setzen Sie das Thread-Flag auf true
    2) Wenn sich der T-Thread in einem blockierten Zustand (Ruhezustand) befindet , wird eine InterruptExeception ausgelöst Das Warten auf Threads ist eine Art Steuerung der Thread-Ausführungssequenz. Dies bedeutet, dass der Thread, der hier wartet, nur die Reihenfolge steuert, in der Threads enden.

    Welcher Thread tritt bei, welcher Thread blockiert und wartet, bis der entsprechende Thread die Ausführung abschließt.

    t.join();

    Der Thread, der diese Methode aufruft, ist der Hauptthread, und diese Methode wird aufgerufen für das Objekt t Time ist es, main auf t warten zu lassen.
    Der Code stoppt, wenn er die Verbindungslinie erreicht, lässt t zuerst enden und dann main fortfahren.

    t.join(10000);join stellt eine weitere Version mit einem Parameter bereit, der Parameter ist wait Nach 10 Sekunden , treten Sie direkt bei und warten Sie nicht mehr aktueller Thread In der Anwendung wird die Instanz dieses Threads wie folgt abgerufen:
    Für diesen Code werden Threads durch Erben der Thread-Methode erstellt. Zu diesem Zeitpunkt erhalten Sie direkt dadurch in der Ausführungsmethode die aktuelle Thread-Instanz

    public class Demo6 {
        public static void main(String[] args) {
            Thread t = new Thread(() ->{
                System.out.println("hello");
            });
            t.start();
        }
    }

    Dies verweist hier jedoch nicht auf den Thread-Typ, sondern auf Ausführbar, was nur eine einfache Aufgabe ist, die kein Namensattribut hat. Um den Thread-Namen zu erhalten, können Sie den Thread-Namen nur über Thread.currentThread()

    public class Demo10 {
        // 通过这个变量来控制线程是否结束.
        private static boolean isQuit = false;
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (!isQuit) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
    
            // 就可以在 main 线程中通过修改 isQuit 的值, 来影响到线程是否退出
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // main 线程在 5s 之后, 修改 isQuit 的状态.
            isQuit = true;
        }
    }

    Prozessstatus abrufen

    Für die Systemebenen:

    ready

    blocking


    #🎜 🎜#

    Die Thread-Klasse in Java wurde weiter verfeinert:



    NEU:

    Thread Das Objekt wurde erstellt, aber start wurde noch nicht aufgerufen

    BEENDET:# 🎜🎜#Der Thread im Betriebssystem wurde ausgeführt und zerstört, aber das Thread-Objekt ist immer noch da. Erhaltener Status

    RUNNABLE: Bereitzustand, der Thread in diesem Zustand befindet sich in der Bereitschaftswarteschlange und kann jederzeit für die CPU geplant werden. Am


    TIME_WAITING:
    wechselt in diesen Zustand, wenn Sleep aufgerufen wird, beitreten (Timeout-Zeit) BLOCKIERT: Der aktuelle Thread wartet auf die Sperre

    WAITING:#🎜 🎜#Der aktuelle Thread wartet darauf, aufzuwachen.

    线程安全问题

    定义:操作系统中线程调度是随机的,导致程序的执行可能会出现一些bug。如果因为调度随机性引入了bug线程就是不安全的,反之则是安全的。
    解决方法:加锁,给方法直接加上synchronized关键字,此时进入方法就会自动加锁,离开方法就会自动解锁。当一个线程加锁成功的时候,其他线程尝试加锁就会触发阻塞等待,阻塞会一直持续到占用锁的线程把锁释放为止。

    synchronized public void increase() {
            count++;
    }

    线程不安全产生的原因:

    • 1.线程是抢占式执行,线程间的调度充满随机性。

    • 2.多个线程对同一个变量进行修改操作

    • 3.针对变量的操作不是原子的

    • 4.内存可见性也会影响线程安全(针对同一个变量t1线程循环进行多次读操作,t2线程少次修改操作,t1就不会从内存读数据了而是从寄存器里读)

    • 5.指令重排序,也是编译器优化的一种操作,保证逻辑不变的情况下调整顺序,解决方法synchronized。

    内存可见性解决方法:

    • 1.使用synchronized关键字 使用synchronized不光能保证指令的原子性,同时也能保证内存的可见性。被synchronized包裹起来的代码编译器就不会从寄存器里读。

    • 2.使用volatile关键字 能够保证内存可见性,禁止编译器作出上述优化,编译器每次执行判定相等都会重新从内存读取。

    synchronized用法

    在java中每个类都是继承自Object,每个new出来的实例里面一方面包含自己安排的属性,另一方面包含了“对象头”即对象的一些元数据。加锁操作就是在这个对象头里面设置一个标志位。

    1.直接修饰普通的方法

    使用synchronized的时候本质上是对某个“对象”进行加锁,此时的锁对象就是this。加锁操作就是在设置this的对象头的标志位,当两个线程同时尝试对同一个对象加锁的时候才有竞争,如果是两个线程在针对两个不同对象加锁就没有竞争。

    class Counter{
    	public int count;
    	synchronized public void increase(){
    	count++;
    	}
    }

    2.修饰一个代码块

    需要显示制定针对那个对象加锁(java中的任意对象都可以作为锁对象)

    public void increase(){
        synchronized(this){
        count++;
        }
    }

    3.修饰一个静态方法

    相当于针对当前类的类对象加锁,类对象就是运行程序的时候。class文件被加载到JVM内存中的模样。

    synchronized public static void func(){
    }

    或者

    public static void func(){
        synchronized(Counter.class){
    
        }
    }

    监视器锁monitor lock

    可重入锁就是同一个线程针对同一个锁,连续加锁两次,如果出现死锁就是不可重入锁,如果不会死锁就是可重入的。因此就把synchronized实现为可重入锁,下面的例子里啊连续加锁操作不会导致死锁。可重入锁内部会记录所被哪个线程占用也会记录加锁次数,因此后续再加锁就不是真的加锁而是单纯地把技术给自增。

    synchronized public void increase(){
        synchronized(this){
            count++;
        }
    }

    死锁的其他场景

    • 1.一个线程一把锁

    • 2.两个线程两把锁

    • 3.N个线程M把锁(哲学家就餐问题,解决方法:先拿编号小的筷子)

    死锁的四个必要条件(前三个都是锁本身的特点)

    • 1.互斥使用,一个锁被另一个线程占用后其他线程就用不了(锁的本质,保证原子性)

    • 2.不可抢占,一个锁被一个线程占用后其他线程不可把这个锁给挖走

    • 3.请求和保持,当一个线程占据了多把锁之后,除非显示的释放否则这些锁中都是该线程持有的

    • 4.环路等待,等待关系成环(解决:遵循固定的顺序加锁就不会出现环路等待)

    java线程类:

    • 不安全的:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StringBuilder

    • 安全的:Vector,HashTable,ConcurrentHashMap,StringBuffer,String

    volatile

    禁止编译器优化保证内存可见性,产生原因:计算机想执行一些计算就需要把内存的数据读到CPU寄存器中,然后再从寄存器中计算写回到内存中,因为CPU访问寄存器的速度比访问内存快很多,当CPU连续多次访问内存结果都一样,CPU就会选择访问寄存器。

    JMM(Java Memory Model)Java内存模型

    就是把硬件结构在java中用专业的术语又重新抽象封装了一遍。

    工作内存(work memory)其实指的不是内存,而是CPU寄存器。
    主内存(main memeory)这才是主内存。
    原因:java作为一个跨平台编程语言要把硬件细节封装起来,假设某个计算机没有CPU或者内存同样可以套到上述模型中。

    寄存器,缓存和内存之间的关系

    CPU从内存取数据太慢,因此把数据直接放到寄存器里来读,但寄存器空间太紧张于是又搞了一个存储空间,比寄存器大比内存小速度比寄存器慢比内存快称为缓存。寄存器和缓存统称为工作内存。

    寄存器,缓存和内存之间的关系图

    Java-Multithreading: Verwendung der Thread-Klasse.

    • 存储空间:CPU

    • 速度:CPU>L1>L2>L3>内存

    • 成本:CPU>L1>L2>L3>内存

    volatile和synchronized的区别

    • volatile只是保证可见性不保证原子性,只是处理一个线程读和一个线程写的过程。

    • synchronized都能处理

    wait和notify

    等待和通知处理线程调度随机性问题的,join也是一种控制顺序的方式更倾向于控制线程结束。wait和notify都是Object对象的方法,调用wait方法的线程就会陷入阻塞,阻塞到有线程通过notify来通知。

    public class Demo9 {
        public static void main(String[] args) throws InterruptedException {
            Object object = new Object();
            System.out.println("wait前");
            object.wait();
            System.out.println("wait后");
        }
    }

    wait内部会做三件事;

    • 1.先释放锁

    • 2.等待其他线程的通知

    • 3.收到通知后重新获得锁并继续往下执行

    因此想用wait/notify就得搭配synchronized

    public class Demo9 {
        public static void main(String[] args) throws InterruptedException {
            Object object = new Object();
            synchronized (object){
                System.out.println("wait前");
                object.wait();
                System.out.println("wait后");
            }
        }
    }

    注意:wait notify都是针对同一对象来操作的,例如现在有一个对象o,有10个线程都调用了o.wait,此时10个线程都是阻塞状态。如果调用了o.notify就会把10个线程中的一个线程唤醒。而notifyAll就会把所有10个线程全都给唤醒,此时就会竞争锁。

    Das obige ist der detaillierte Inhalt vonJava-Multithreading: Verwendung der Thread-Klasse.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen