Heim  >  Artikel  >  Java  >  Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden

Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden

php是最好的语言
php是最好的语言Original
2018-07-26 14:42:391391Durchsuche

Was ist der Unterschied zwischen Thread und Prozess? Antwort: Ein Prozess ist eine unabhängige laufende Umgebung, die als Programm oder Anwendung betrachtet werden kann. Ein Thread ist eine Aufgabe, die in einem Prozess ausgeführt wird. Threads sind eine Teilmenge von Prozessen. Ein Prozess kann viele Threads haben und jeder Thread führt unterschiedliche Aufgaben parallel aus. Verschiedene Prozesse nutzen unterschiedliche Speicherbereiche und alle Threads teilen sich denselben Speicherplatz.

1. Der Unterschied zwischen Threads und Prozessen:

1) Thread: die Ausführungseinheit, die für die Programmausführung im Prozess verantwortlich ist
Der Thread selbst ist auf die Ausführung des Programms angewiesen
Ein Thread ist ein Programm. Der sequentielle Kontrollfluss kann nur die Ressourcen und die Umgebung nutzen, die dem Programm zugewiesen sind

2) Prozess: das ausführende Programm
Ein Prozess enthält mindestens einen Thread

3) Einzelner Thread: Es gibt nur einen Thread im Programm. Tatsächlich ist die Hauptmethode ein Hauptthread

4) Multithreading: Ausführen mehrerer Aufgaben in einem Programm
Der Zweck ist eine bessere Nutzung der CPU-Ressourcen

2. Thread-Implementierung

1. Erben Sie die Thread-Klasse Zum Erstellen muss die run()-Methode

class MyThread extends Thread{
    private static int num = 0;    public MyThread(){
        num++;
    }

    @Override    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

überschrieben werden. Nachdem Sie Ihre eigene Thread-Klasse erstellt haben, können Sie ein Thread-Objekt erstellen und den Thread dann über die start()-Methode starten. Beachten Sie, dass die run()-Methode nicht zum Starten des Threads aufgerufen wird. Wenn die run-Methode aufgerufen wird, entspricht dies der Ausführung der run-Methode im Hauptthread Es gibt keinen Unterschied zu gewöhnlichen Methodenaufrufen. Zu diesem Zeitpunkt wird kein neuer Thread erstellt, um die definierten Aufgaben auszuführen.

public class Test {
    public static void main(String[] args)  {
        MyThread thread = new MyThread();
        thread.start();
    }
}class MyThread extends Thread{
    private static int num = 0;    public MyThread(){
        num++;
    }
    @Override    public void run() {
        System.out.println("主动创建的第"+num+"个线程");
    }
}

Im obigen Code wird durch Aufrufen der start()-Methode ein neuer Thread erstellt. Um den Unterschied zwischen dem Methodenaufruf start() und dem Methodenaufruf run() zu unterscheiden, sehen Sie sich bitte das folgende Beispiel an:

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}class MyThread extends Thread{
    private String name;    public MyThread(String name){        this.name = name;
    }

    @Override    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}

Laufergebnisse:


Aus den Ausgabeergebnissen ergeben sich die folgenden Schlussfolgerungen kann gezeichnet werden: Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden

1) Die Thread-IDs von Thread1 und Thread2 sind unterschiedlich und die ID von Thread2 und dem Haupt-Thread ist gleich. Dies bedeutet, dass beim Aufrufen der Ausführungsmethode kein neuer Thread erstellt wird Führt die Ausführungsmethode direkt im Hauptthread aus, was sich von gewöhnlichen Methodenaufrufen unterscheidet Der Aufruf der Ausführungsmethode von Thread2 wird zuerst ausgegeben, was darauf hinweist, dass der Prozess der Erstellung eines neuen Threads die nachfolgende Ausführung des Hauptthreads nicht blockiert.

2. Implementieren Sie die Runnable-Schnittstelle

Zusätzlich zum Erben der Thread-Klasse beim Erstellen eines Threads in Java können Sie auch ähnliche Funktionen implementieren, indem Sie die Runnable-Schnittstelle implementieren. Die Implementierung der Runnable-Schnittstelle muss deren Ausführungsmethode überschreiben. Das Folgende ist ein Beispiel:

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
} 
class MyRunnable implements Runnable{
    public MyRunnable() {
    }

    @Override    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

Die chinesische Bedeutung von Runnable ist „Aufgabe“. Wie der Name schon sagt, definieren wir durch die Implementierung der Runnable-Schnittstelle eine Unteraufgabe und übergeben die Unteraufgabe dann an Thread Ausführung. Beachten Sie, dass diese Methode Runnable als Parameter der Thread-Klasse verwenden und dann die Startmethode von Thread verwenden muss, um einen neuen Thread zum Ausführen der Unteraufgabe zu erstellen. Wenn Sie die run-Methode von Runnable aufrufen, wird kein neuer Thread erstellt. Dieser normale Methodenaufruf macht keinen Unterschied.

Wenn Sie sich den Implementierungsquellcode der Thread-Klasse ansehen, werden Sie feststellen, dass die Thread-Klasse die Runnable-Schnittstelle implementiert.

In Java können diese beiden Methoden verwendet werden, um Threads zur Ausführung von Unteraufgaben zu erstellen. Welche Methode Sie wählen, hängt von Ihren eigenen Anforderungen ab. Das direkte Erben der Thread-Klasse scheint einfacher zu sein als das Implementieren der Runnable-Schnittstelle. Da Java jedoch nur eine einzelne Vererbung zulässt, kann eine benutzerdefinierte Klasse, wenn sie andere Klassen erben muss, nur die Implementierung der Runnable-Schnittstelle auswählen.

3. Verwenden Sie ExecutorService, Callable und Future, um Multithreading mit zurückgegebenen Ergebnissen zu implementieren.

Mehr über Multithreading erfahren Sie später Methode.

ExecutorService-, Callable- und Future-Objekte gehören tatsächlich zu Funktionsklassen im Executor-Framework. Wenn Sie mehr über das Executor-Framework erfahren möchten, können Sie http://www.javaeye.com/topic/366591 besuchen, wo das Framework ausführlich erklärt wird. Der Thread, der das Ergebnis zurückgibt, ist eine neue Funktion, die in JDK1.5 eingeführt wurde. Mit dieser Funktion muss ich mir keine großen Mühen mehr machen, und selbst wenn sie implementiert ist. es kann voller Schlupflöcher sein.

Aufgaben, die einen Wert zurückgeben können, müssen die Callable-Schnittstelle implementieren. Ebenso müssen Aufgaben, die keinen Wert zurückgeben, die Runnable-Schnittstelle implementieren. Nach dem Ausführen der Callable-Aufgabe können Sie ein Future-Objekt abrufen, indem Sie get für das Objekt aufrufen. In Kombination mit der Thread-Pool-Schnittstelle ExecutorService können Sie das legendäre Multithreading implementieren, das Ergebnisse zurückgibt . Nachfolgend finden Sie ein vollständiges Multi-Thread-Testbeispiel mit den zurückgegebenen Ergebnissen. Es wurde unter JDK1.5 verifiziert und kann problemlos direkt verwendet werden. Der Code lautet wie folgt:

/**
* 有返回值的线程 
*/ @SuppressWarnings("unchecked")  
public class Test {  public static void main(String[] args) throws ExecutionException,  
    InterruptedException {  
   System.out.println("----程序开始运行----");  
   Date date1 = new Date();  

   int taskSize = 5;  
   // 创建一个线程池  
   ExecutorService pool = Executors.newFixedThreadPool(taskSize);  
   // 创建多个有返回值的任务  
   List<Future> list = new ArrayList<Future>();  
   for (int i = 0; i < taskSize; i++) {  
    Callable c = new MyCallable(i + " ");  
    // 执行任务并获取Future对象  
    Future f = pool.submit(c);  
    // System.out.println(">>>" + f.get().toString());  
    list.add(f);  
   }  
   // 关闭线程池  
   pool.shutdown();  

   // 获取所有并发任务的运行结果  
   for (Future f : list) {  
    // 从Future对象上获取任务的返回值,并输出到控制台  
    System.out.println(">>>" + f.get().toString());  
   }  

   Date date2 = new Date();  
   System.out.println("----程序结束运行----,程序运行时间【" 
     + (date2.getTime() - date1.getTime()) + "毫秒】");  
}  
}  

class MyCallable implements Callable<Object> {  
private String taskNum;  

MyCallable(String taskNum) {  
   this.taskNum = taskNum;  
}  

public Object call() throws Exception {  
   System.out.println(">>>" + taskNum + "任务启动");  
   Date dateTmp1 = new Date();  
   Thread.sleep(1000);  
   Date dateTmp2 = new Date();  
   long time = dateTmp2.getTime() - dateTmp1.getTime();  
   System.out.println(">>>" + taskNum + "任务终止");  
   return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";  
}
}

Codebeschreibung:

Die Executors-Klasse im obigen Code stellt eine Reihe von Factory-Methoden zum Erstellen von Thread-Pools bereit, und die zurückgegebenen Thread-Pools implementieren alle die ExecutorService-Schnittstelle.

public static ExecutorService newFixedThreadPool(int nThreads)

Erstellen Sie einen Thread-Pool mit einer festen Anzahl von Threads.

public static ExecutorService newCachedThreadPool()
Erstellen Sie einen zwischenspeicherbaren Thread-Pool. Durch den Aufruf von Execute wird der zuvor erstellte Thread wiederverwendet (sofern der Thread verfügbar ist). Wenn kein vorhandener Thread verfügbar ist, wird ein neuer Thread erstellt und dem Pool hinzugefügt. Beenden und entfernen Sie die Threads aus dem Cache, die 60 Sekunden lang nicht verwendet wurden.

public static ExecutorService newSingleThreadExecutor()
Erstellen Sie einen Single-Threaded-Executor.

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
Erstellen Sie einen Thread-Pool, der die geplante und periodische Aufgabenausführung unterstützt, der in den meisten Fällen als Ersatz für die Timer-Klasse verwendet werden kann.

ExecutoreService stellt die subscribe()-Methode bereit, übergibt ein Callable oder Runnable und gibt ein Future zurück. Wenn der Executor-Hintergrund-Thread-Pool die Berechnung von Callable noch nicht abgeschlossen hat, gibt dieser Aufruf die get()-Methode des Future-Objekts zurück, die blockiert, bis die Berechnung abgeschlossen ist.

3. Thread-Status

Bevor wir die spezifischen Methoden in der Thread-Klasse lernen, sollten wir uns zunächst mit dem Status des Threads befassen Verfahren.

1) Zustand „Erstellen“ (neu): Ein Multithread-Objekt ist bereit
2) Zustand „Bereit“ (ausführbar): Die start()-Methode wird aufgerufen und wartet auf die CPU-Planung
3) Ausführungsstatus (läuft): Führen Sie die run()-Methode aus
4) Blockierter (blockierter) Status: Stoppen Sie die Ausführung vorübergehend und geben Sie möglicherweise Ressourcen an andere Threads zur Verwendung weiter
5) Beendigungsstatus (tot): Der Thread ist zerstört
wann Wenn ein neuer Thread gestartet werden muss, um eine bestimmte Unteraufgabe auszuführen, wird ein Thread erstellt. Nachdem der Thread erstellt wurde, wechselt er jedoch nicht sofort in den Bereitschaftszustand, da für die Ausführung des Threads einige Bedingungen erforderlich sind (z. B. Speicherressourcen). Im vorherigen Blogbeitrag zur JVM-Speicherbereichsaufteilung wissen wir, dass der Programmzähler Der Java-Stack und der lokale Methoden-Stack sind alle für den Thread privat, daher muss dem Thread eine bestimmte Menge an Speicherplatz zugewiesen werden. Erst wenn alle für die Ausführung des Threads erforderlichen Bedingungen erfüllt sind, wird er in den Bereitschaftszustand versetzt Zustand.

Wenn ein Thread in den Bereitschaftszustand wechselt, bedeutet dies nicht, dass die CPU-Ausführungszeit sofort abgerufen werden kann. Möglicherweise führt die CPU zu diesem Zeitpunkt andere Dinge aus und muss daher warten. Wenn die CPU-Ausführungszeit erreicht ist, wechselt der Thread tatsächlich in den Betriebszustand.

Während der Thread ausgeführt wird, kann es mehrere Gründe geben, warum der aktuelle Thread nicht weiter ausgeführt wird, z. B. wenn der Benutzer den Thread aktiv in den Ruhezustand versetzt (und ihn dann nach einer bestimmten Zeit im Ruhezustand erneut ausführt). , der Benutzer lässt den Thread aktiv warten oder wird durch einen Synchronisationsblock blockiert, was mehreren Zuständen entspricht: Zeitwarten (Schlafen oder Warten auf ein bestimmtes Ereignis), Warten (Warten darauf, geweckt zu werden) und blockiert (blockiert).

Wenn die Ausführung einer Unteraufgabe aufgrund einer plötzlichen Unterbrechung abgeschlossen ist, wird der Thread beendet.

Das folgende Bild beschreibt den Zustand eines Threads von der Erstellung bis zum Tod:
Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-MethodenIn einigen Tutorials werden Blockiert, Warten und Zeitwarten zusammenfassend als Blockierungszustände bezeichnet. Dies ist auch möglich. solange Aber hier möchte ich den Status des Threads mit dem Methodenaufruf in Java verknüpfen, also trenne ich die beiden Zustände Warten und Zeitwarten.

Hinweis: Der Unterschied zwischen Sleep und Wait:

Sleep ist eine Methode der Thread-Klasse, Wait ist eine in der Object-Klasse definierte Methode Verursacht keine Änderungen im Sperrverhalten. Thread.sleep lässt den Thread die Sperre nicht frei.
Thread.sleep und Object.wait unterbrechen beide die Ausführung Zeit für andere Threads. Der Unterschied besteht darin, dass andere Threads nach dem Aufruf von „notify/notifyAll“ die CPU-Ausführungszeit zurückgewinnen müssen Dies versteht sich (für einen Kern) und die CPU kann jeweils nur einen Thread ausführen. Wenn ein Thread ausgeführt wird, wird dies als Thread-Kontextwechsel bezeichnet (dasselbe gilt für Prozesse).

Da die Aufgabe des aktuellen Threads möglicherweise nicht abgeschlossen wird, muss der Ausführungsstatus des Threads beim Umschalten gespeichert werden, damit Sie beim nächsten Zurückschalten im vorherigen Status weiterlaufen können. Um ein einfaches Beispiel zu geben: Zum Beispiel liest ein Thread A den Inhalt einer Datei und liest die Hälfte der Datei. Zu diesem Zeitpunkt muss Thread A angehalten und an die Ausführung von Thread B übergeben werden A wiederum tun wir dies nicht. Wir hoffen, dass Thread A erneut vom Anfang der Datei liest.

Daher muss der Laufstatus von Thread A aufgezeichnet werden. Da Sie vor dem nächsten Mal wissen müssen, welche Anweisung der aktuelle Thread ausgeführt hat, müssen Sie den Wert des Programmzählers aufzeichnen. Wenn der Thread beispielsweise angehalten wird, während er eine bestimmte Berechnung durchführt, wird die Ausführung unterbrochen Fahren Sie beim nächsten Mal fort. Manchmal müssen Sie den Wert der Variablen kennen, als sie angehalten wurde, sodass Sie den Status des CPU-Registers aufzeichnen müssen. Im Allgemeinen werden Daten wie der Programmzähler und der CPU-Registerstatus während des Thread-Kontextwechselprozesses aufgezeichnet.

Um es einfach auszudrücken: Der Kontextwechsel für einen Thread ist eigentlich der Prozess des Speicherns und Wiederherstellens des CPU-Status, der es der Thread-Ausführung ermöglicht, die Ausführung an der Unterbrechungsstelle fortzusetzen.

虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。

四、线程的常用方法

Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-MethodenEin nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden1.静态方法

currentThread()方法

currentThread()方法可以返回代码段正在被哪个线程调用的信息。

public class Run1{    public static void main(String[] args){                 
    System.out.println(Thread.currentThread().getName());
    }
}

2.sleep()方法

方法sleep()的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。

sleep方法有两个重载版本:

sleep(long millis)     //参数为毫秒
sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。
但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。看下面这个例子就清楚了:

public class Test {    private int i = 10;    private Object object = new Object();    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread1 = test.new MyThread();
        MyThread thread2 = test.new MyThread();
        thread1.start();
        thread2.start();
    } 

    class MyThread extends Thread{
        @Override        public void run() {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);                try {
                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                    Thread.currentThread().sleep(10000);
                } catch (InterruptedException e) {                    // TODO: handle exception
                }
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}

Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。

注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。

3.yield()方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

public class MyThread  extends Thread{
    @Override
    public void run() {        long beginTime=System.currentTimeMillis();        int count=0;        for (int i=0;i<50000000;i++){
            count=count+(i+1);            //Thread.yield();
        }        long endTime=System.currentTimeMillis();
        System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
    }
}public class Run {
    public static void main(String[] args) {
        MyThread t= new MyThread();
        t.start();
    }
}

执行结果:
用时:3 毫秒!

如果将 //Thread.yield();的注释去掉,执行结果如下:
用时:16080 毫秒!

对象方法

4.start()方法
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

5.run()方法
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

5.getId()
getId()的作用是取得线程的唯一标识
代码:

public class Test {    public static void main(String[] args) {
        Thread t= Thread.currentThread();
        System.out.println(t.getName()+" "+t.getId());
    }
}

输出:
main 1

6.isAlive()方法
方法isAlive()的功能是判断当前线程是否处于活动状态
代码:

public class MyThread  extends Thread{
    @Override
    public void run() {
        System.out.println("run="+this.isAlive());
    }
}public class RunTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        System.out.println("begin =="+myThread.isAlive());
        myThread.start();
        System.out.println("end =="+myThread.isAlive());
    }
}

程序运行结果:

begin ==falserun=trueend ==false

方法isAlive()的作用是测试线程是否偶处于活动状态。什么是活动状态呢?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。
有个需要注意的地方
System.out.println(“end ==”+myThread.isAlive());

虽然上面的实例中打印的值是true,但此值是不确定的。打印true值是因为myThread线程还未执行完毕,所以输出true。如果代码改成下面这样,加了个sleep休眠:

public static void main(String[] args) throws InterruptedException {
        MyThread myThread=new MyThread();
        System.out.println("begin =="+myThread.isAlive());
        myThread.start();
        Thread.sleep(1000);
        System.out.println("end =="+myThread.isAlive());
    }

则上述代码运行的结果输出为false,因为mythread对象已经在1秒之内执行完毕。

7.join()方法
在很多情况下,主线程创建并启动了线程,如果子线程中药进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。

public class Thread4 extends Thread{
    public Thread4(String name) {        super(name);
    }    public void run() {        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + "  " + i);
        }
    }    public static void main(String[] args) throws InterruptedException {        // 启动子进程
        new Thread4("new thread").start();        for (int i = 0; i < 10; i++) {            if (i == 5) {
                Thread4 th = new Thread4("joined thread");
                th.start();
                th.join();
            }
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }
}

执行结果:

main  0main  1main  2main  3main  4new thread  0new thread  1new thread  2new thread  3new thread  4joined thread  0joined thread  1joined thread  2joined thread  3joined thread  4main  5main  6main  7main  8main  9

由上可以看出main主线程等待joined thread线程先执行完了才结束的。如果把th.join()这行注释掉,运行结果如下:

main  0main  1main  2main  3main  4main  5main  6main  7main  8main  9new thread  0new thread  1new thread  2new thread  3new thread  4joined thread  0joined thread  1joined thread  2joined thread  3joined thread  4

8.其他方法
getName和setName
用来得到或者设置线程名称。

getPriority和setPriority
用来获取和设置线程优先级。

setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

在上面已经说到了Thread类中的大部分方法,那么Thread类中的方法调用到底会引起线程状态发生怎样的变化呢?下面一幅图就是在上面的图上进行改进而来的:
Ein nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden

五、停止线程

停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理。
停止一个线程可以使用Thread.stop()方法,但最好不用它。该方法是不安全的,已被弃用。
在Java中有以下3种方法可以终止正在运行的线程:

1.使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
2.使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
3.暂停线程

interrupt()方法

六、线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。
设置线程优先级有助于帮“线程规划器”确定在下一次选择哪一个线程来优先执行。
设置线程的优先级使用setPriority()方法,此方法在JDK的源码如下:

public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {            throw new IllegalArgumentException();
        }        if((g = getThreadGroup()) != null) {            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

在Java中,线程的优先级分为1~10这10个等级,如果小于1或大于10,则JDK抛出异常throw new IllegalArgumentException()。
JDK中使用3个常量来预置定义优先级的值,代码如下:

public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;

1.线程优先级特性:

1)继承性
比如A线程启动B线程,则B线程的优先级与A是一样的。
2)规则性
高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。
3)随机性
优先级较高的线程不一定每一次都先执行完。

七、守护线程

在Java线程中有两种线程,一种是User Thread(用户线程),另一种是Daemon Thread(守护线程)。
Daemon的作用是为其他线程的运行提供服务,比如说GC线程。其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。

守护线程并非虚拟机内部可以提供,用户也可以自行的设定守护线程,方法:public final void setDaemon(boolean on) ;但是有几点需要注意:

thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 (备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)
在Daemon线程中产生的新线程也是Daemon的。 (这一点又是有着本质的区别了:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是“父进程挂掉,init收养,然后文件0,1,2都是/dev/null,当前目录到/”)
不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。

八、同步与死锁

1.同步代码块
在代码块上加上”synchronized”关键字,则此代码块就称为同步代码块
同步代码块格式

synchronisiert(synchronisiertes Objekt){
Codeblöcke, die synchronisiert werden müssen;
}
2. Synchronisationsmethoden
Zusätzlich zu synchronisierbaren Codeblöcken können auch Methoden synchronisiert werden
Methodensynchronisationsformat
1
synchronisierter Void-Methodenname (){}

Verwandte Artikel:

Java – Multithreading

Java-Instanz – Thread hängt

Ähnliche Videos:

Video-Tutorial für erweiterte Anwendungen der Java Multithreading and Concurrency Library

Das obige ist der detaillierte Inhalt vonEin nullbasiertes Verständnis der Implementierung von Java-Threads und eine Sammlung häufig verwendeter Thread-Methoden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn