Heim > Java > JavaBase > Hauptteil

Mehrere Möglichkeiten, Multithreading in Java zu implementieren

青灯夜游
Freigeben: 2023-01-04 15:52:49
Original
9441 Leute haben es durchsucht

So implementieren Sie Multithreading: 1. Erben Sie die Thread-Klasse und schreiben Sie die Ausführungsmethode der Thread-Klasse über die von JDK bereitgestellte Thread-Klasse neu. 2. Implementieren Sie die Runnable-Schnittstelle. Runnable ist eine funktionale Schnittstelle „@FunctionalInterface“. Dies bedeutet, dass Sie die von JDK8 bereitgestellte Lambda-Methode verwenden können. 3. Verwenden Sie interne Klassen. 5. Thread-Implementierung mit Rückgabewerten.

Mehrere Möglichkeiten, Multithreading in Java zu implementieren

Die Betriebsumgebung dieses Tutorials: Windows7-System, Java8-Version, DELL G3-Computer.

Es gibt zwei Hauptmethoden, um Multithreading in Formularen zu implementieren: Eine besteht darin, die Thread-Klasse zu erben, und die andere darin, die Runnable-Schnittstelle zu implementieren. Im Wesentlichen besteht die Implementierungsmethode darin, die Thread-Aufgabe zu implementieren und dann den Thread zu starten, um die Thread-Aufgabe auszuführen (die Thread-Aufgabe ist hier eigentlich die Ausführungsmethode). Bei den hier genannten 6 Typen handelt es sich tatsächlich um Modifikationen, die auf den beiden oben genannten Typen basieren.

Das Folgende ist eine Einführung in diese 6 Implementierungsmethoden nacheinander.

Der erste Weg: Erben Sie die Thread-Klasse

Alles ist ein Objekt, dann sind Threads auch Objekte, und Objekte sollten in der Lage sein, ihre öffentlichen Eigenschaften zu extrahieren und sie in Klassen zu kapseln, um mehrere zu instanziieren Objekte und dann implementieren. Die erste Möglichkeit, Threads zu verwenden, besteht darin, „die Thread-Klasse zu erben“. Das Erben der Thread-Klasse ist die einfachste Möglichkeit, Threads zu implementieren. Schreiben Sie einfach die Ausführungsmethode der Thread-Klasse über die vom JDK bereitgestellte Thread-Klasse um. Wenn der Thread dann gestartet wird, wird der Inhalt des Ausführungsmethodenkörpers ausgeführt. Der Code lautet wie folgt:

package com.kingh.thread.create;

/**
 * 继承Thread类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/13 19:19
 */
public class CreateThreadDemo1 extends Thread {

    public CreateThreadDemo1() {
        // 设置当前线程的名字
        this.setName("MyThread");
    }

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateThreadDemo1().start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}
Nach dem Login kopieren
Die laufenden Ergebnisse sind wie folgt

当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: MyThread
当前运行的线程名为: main
Nach dem Login kopieren

Hier ist zu beachten, dass beim Starten eines Threads nicht die Ausführungsmethode der Thread-Klasse aufgerufen wird, sondern die Startmethode der Thread-Klasse angerufen. Können wir also die run-Methode aufrufen? Die Antwort lautet „Ja“, da die Ausführungsmethode eine öffentlich deklarierte Methode ist, sodass wir sie aufrufen können. Wenn wir jedoch die Ausführungsmethode aufrufen, wird diese Methode als normale Methode aufgerufen und startet den Thread nicht. Dabei wird tatsächlich das Vorlagenmethodenmuster im Entwurfsmuster verwendet. Die Thread-Klasse dient als Vorlage, und die Ausführungsmethode ändert sich, sodass sie in einer Unterklasse implementiert wird.

1. Erstellen Sie mehrere ThreadsIm obigen Beispiel gibt es zusätzlich zu dem einen Thread, den wir erstellt haben, tatsächlich einen Hauptthread, der auch ausgeführt wird. Gibt es neben diesen beiden Threads tatsächlich noch andere Threads, die ausgeführt werden? Beispielsweise wird der „Garbage-Collection-Thread“, den wir nicht sehen können, ebenfalls stillschweigend ausgeführt. Hier berücksichtigen wir nicht, wie viele Threads ausgeführt werden. Wir haben oben selbst einen Thread erstellt. Können wir also ein paar weitere erstellen und diese gemeinsam ausführen? Eine Thread-Klasse ist ein Thread-Objekt. Erstellen Sie daher mehrere Thread-Klassen und rufen Sie deren Startmethode auf, um mehrere Threads zu starten. Der Code lautet wie folgt

package com.kingh.thread.create;

/**
 * 创建多个线程同时执行
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo2 extends Thread {

    public CreateMultiThreadDemo2(String name) {
        // 设置当前线程的名字
        this.setName(name);
    }

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateMultiThreadDemo2("MyThread-01").start();

        // 创建多个线程实例,同时执行
        new CreateMultiThreadDemo2("MyThread-02").start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}
Nach dem Login kopieren

Die laufenden Ergebnisse sind wie folgt

2. Geben Sie den Thread-Namen an

Sie können sehen, dass durch das Erstellen mehrerer Thread-Klassen und den Aufruf ihrer Startmethode mehrere Threads gestartet werden. Jeder Thread hat seinen eigenen Namen. Im obigen Code werden den erstellten Threads die Namen MyThread-01 und MyThread-02 zugewiesen. Anschließend wird der Thread-Name dem Thread-Namen zugewiesen, indem die setName-Methode der übergeordneten Klasse aufgerufen wird Konstruktormethode. Wenn Sie keinen Thread-Namen angeben, gibt das System standardmäßig den Thread-Namen an und die Benennungsregel hat die Form Thread-N. Um die Fehlerbehebung zu erleichtern, wird jedoch empfohlen, beim Erstellen eines Threads einen sinnvollen Thread-Namen anzugeben. Der folgende Code verwendet nicht den Thread-Namen
当前运行的线程名为: main
当前运行的线程名为: MyThread-02
当前运行的线程名为: MyThread-01
当前运行的线程名为: main
当前运行的线程名为: MyThread-01
当前运行的线程名为: MyThread-02
当前运行的线程名为: main
Nach dem Login kopieren
Das laufende Ergebnis lautet wie folgt:
package com.kingh.thread.create;

/**
 * 创建多个线程同时执行,使用系统默认线程名
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 9:46
 */
public class CreateMultiThreadDemo3 extends Thread {

    @Override
    public void run() {
        // 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // 注意这里,要调用start方法才能启动线程,不能调用run方法
        new CreateMultiThreadDemo3().start();

        // 创建多个线程实例,同时执行
        new CreateMultiThreadDemo3().start();

        // 演示主线程继续向下执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}
Nach dem Login kopieren

Zweiter Weg: Implementieren Sie die Runnable-Schnittstelle

Die Implementierung der Runnable-Schnittstelle ist auch eine gängige Methode zum Erstellen von Threads. Die Verwendung von Schnittstellen kann die Kopplung in unseren Programmen reduzieren. In der Runnable-Schnittstelle ist nur eine Methode definiert, die ausgeführt wird. Werfen wir einen Blick auf den Code der Runnable-Schnittstelle.

当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0
当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0
Nach dem Login kopieren
Tatsächlich ist Runnable eine Thread-Aufgabe und Thread-Steuerung. Dies ist die oben erwähnte Entkopplung. Wenn wir einen Thread implementieren möchten, können wir die Thread-Klasse verwenden. Die von der Thread-Klasse auszuführenden Aufgaben können von Klassen verarbeitet werden, die die Runnable-Schnittstelle implementieren.

Das ist die Essenz von Runnable!

Runnable ist eine @FunctionalInterface-Funktionsschnittstelle, was bedeutet, dass Sie die von JDK8 bereitgestellte Lambda-Methode verwenden können, um Thread-Aufgaben zu erstellen. Der folgende Code zeigt den Lesern, wie sie verwendet werden.

Die Schritte zur Verwendung von Runnable zur Implementierung des obigen Beispiels sind wie folgt:
  • 定义一个类实现Runnable接口,作为线程任务类
  • 重写run方法,并实现方法体,方法体的代码就是线程所执行的代码
  • 定义一个可以运行的类,并在main方法中创建线程任务类
  • 创建Thread类,并将线程任务类做为Thread类的构造方法传入
  • 启动线程

1. 创建线程任务

线程任务就是线程要做的事情,这里我们让这个线程每隔1s中打印自己的名字

package com.kingh.thread.create;

/**
 * 线程任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Task implements Runnable {

    @Override
    public void run() {
		// 每隔1s中输出一次当前线程的名字
        while (true) {
            // 输出线程的名字,与主线程名称相区分
            printThreadInfo();
            try {
                // 线程休眠一秒
                Thread.sleep(1000);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}
Nach dem Login kopieren

2. 创建可运行类

在这里创建线程,并把任务交给线程处理,然后启动线程。

package com.kingh.thread.create;

/**
 * 创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo4_Main {

    public static void main(String[] args) throws Exception {
        // 实例化线程任务类
        CreateThreadDemo4_Task task = new CreateThreadDemo4_Task();

        // 创建线程对象,并将线程任务类作为构造方法参数传入
        new Thread(task).start();

        // 主线程的任务,为了演示多个线程一起执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
    }
}
Nach dem Login kopieren

线程任务和线程的控制分离,那么一个线程任务可以提交给多个线程来执行。这是很有用的,比如车站的售票窗口,每个窗口可以看做是一个线程,他们每个窗口做的事情都是一样的,也就是售票。这样我们程序在模拟现实的时候就可以定义一个售票任务,让多个窗口同时执行这一个任务。那么如果要改动任务执行计划,只要修改线程任务类,所有的线程就都会按照修改后的来执行。相比较继承Thread类的方式来创建线程的方式,实现Runnable接口是更为常用的。

3. lambda方式创建线程任务

这里就是为了简化内部类的编写,简化了大量的模板代码,显得更加简洁。如果读者看不明白,可以读完内部类方式之后,回过来再看这段代码。

package com.kingh.thread.create;

/**
 * 创建线程with lambda
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo5_Lambda {

    public static void main(String[] args) throws Exception {
        // 使用lambda的形式实例化线程任务类
        Runnable task = () -> {
            while (true) {
                // 输出线程的名字
                printThreadInfo();
            }
        };

        // 创建线程对象,并将线程任务类作为构造方法参数传入
        new Thread(task).start();

        // 主线程的任务,为了演示多个线程一起执行
        while (true) {
            printThreadInfo();
            Thread.sleep(1000);
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
Nach dem Login kopieren

第三种方式:使用内部类的方式

这并不是一种新的实现线程的方式,只是另外的一种写法。比如有些情况我们的线程就想执行一次,以后就用不到了。那么像上面两种方式(继承Thread类和实现Runnable接口)都还要再定义一个类,显得比较麻烦,我们就可以通过匿名内部类的方式来实现。使用内部类实现依然有两种,分别是继承Thread类和实现Runnable接口。代码如下:

package com.kingh.thread.create;

/**
 * 匿名内部类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo6_Anonymous {

    public static void main(String[] args) {
        // 基于子类的方式
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }.start();

        // 基于接口的实现
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }).start();
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}
Nach dem Login kopieren

可以想象一下,我能不能既基于接口,又基于子类呢?像下面的代码会执行出什么样子呢?

package com.kingh.thread.create;

/**
 * 匿名内部类的方式创建线程
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo7_Anonymous {

    public static void main(String[] args) {
        // 基于子类和接口的方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printInfo("interface");
                }
            }
        }) {
            @Override
            public void run() {
                while (true) {
                    printInfo("sub class");
                }
            }
        }.start();
    }

    /**
     * 输出当前线程的信息
     */
    private static void printInfo(String text) {
        System.out.println(text);
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}
Nach dem Login kopieren

运行结果如下:

sub class
sub class
Nach dem Login kopieren

我们可以看到,其实是基于子类的执行了,为什么呢,其实很简单,我们先来看一下为什么不基于子类的时候Runnable的run方法可以执行。这个要从Thread的源码看起,下面是我截取的代码片段。

public Thread(Runnable target)
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize) {
    init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it&#39;s an applet or not */

        /* If there is a security manager, ask the security manager
               what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

        /* If the security doesn&#39;t have a strong opinion of the matter
               use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

    /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
    g.checkAccess();

    /*
         * Do we have the required permissions?
         */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
        acc != null ? acc : AccessController.getContext();
    this.target = target; // 注意这里
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}
Nach dem Login kopieren

其实上面的众多代码就是为了表现 this.target = target 那么target是什么呢,是Thread类的成员变量。那么在什么地方用到了target呢?下面是run方法的内容。

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}
Nach dem Login kopieren

我们可以看到,如果通过上面的构造方法传入target,那么就会执行target中的run方法。可能有朋友就会问了,我们同时继承Thread类和实现Runnable接口,target不为空,那么为何不执行target的run呢。不要忘记了,我们在子类中已经重写了Thread类的run方法,因此run方法已经不在是我们看到的这样了。那当然也就不回执行target的run方法。

lambda 方式改造

刚才使用匿名内部类,会发现代码还是比较冗余的,lambda可以大大简化代码的编写。用lambda来改写上面的基于接口的形式的代码,如下

// 使用lambda的形式
new Thread(() -> {
    while (true) {
        printThreadInfo();
    }
}).start();


// 对比不使用lambda的形式
new Thread(new Runnable() {
    @Override
    public void run() {
        while (true) {
            printThreadInfo();
        }
    }
}).start();
Nach dem Login kopieren

第四种方式:定时器

定时器可以说是一种基于线程的一个工具类,可以定时的来执行某个任务。在应用中经常需要定期执行一些操作,比如要在凌晨的时候汇总一些数据,比如要每隔10分钟抓取一次某个网站上的数据等等,总之计时器无处不在。

在Java中实现定时任务有很多种方式,JDK提供了Timer类来帮助开发者创建定时任务,另外也有很多的第三方框架提供了对定时任务的支持,比如Spring的schedule以及著名的quartz等等。因为Spring和quartz实现都比较重,依赖其他的包,上手稍微有些难度,不在本篇博客的讨论范围之内,这里就看一下JDK所给我们提供的API来实现定时任务。

1. 指定时间点执行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo9_Timer {

    private static final SimpleDateFormat format =
            new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    public static void main(String[] args) throws Exception {

        // 创建定时器
        Timer timer = new Timer();

        // 提交计划任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行了...");
            }
        }, format.parse("2017-10-11 22:00:00"));
    }
}
Nach dem Login kopieren

2.间隔时间重复执行

package com.kingh.thread.create;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 定时任务
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo10_Timer {

    public static void main(String[] args){

        // 创建定时器
        Timer timer = new Timer();

        // 提交计划任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行了...");
            }
        },
                new Date(), 1000);
    }
}
Nach dem Login kopieren

关于Spring的定时任务,可以参考 《Spring计划任务》

第五种方式:带返回值的线程实现方式

我们发现上面提到的不管是继承Thread类还是实现Runnable接口,发现有两个问题,第一个是无法抛出更多的异常,第二个是线程执行完毕之后并无法获得线程的返回值。那么下面的这种实现方式就可以完成我们的需求。这种方式的实现就是我们后面要详细介绍的Future模式,只是在jdk5的时候,官方给我们提供了可用的API,我们可以直接使用。但是使用这种方式创建线程比上面两种方式要复杂一些,步骤如下。

  • 创建一个类实现Callable接口,实现call方法。这个接口类似于Runnable接口,但比Runnable接口更加强大,增加了异常和返回值。

  • 创建一个FutureTask,指定Callable对象,做为线程任务。

  • 创建线程,指定线程任务。

  • 启动线程

代码如下:

package com.kingh.thread.create;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 带返回值的方式
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo11_Callable {

    public static void main(String[] args) throws Exception {

        // 创建线程任务
        Callable<Integer> call = () -> {
            System.out.println("线程任务开始执行了....");
            Thread.sleep(2000);
            return 1;
        };

        // 将任务封装为FutureTask
        FutureTask<Integer> task = new FutureTask<>(call);

        // 开启线程,执行线程任务
        new Thread(task).start();

        // ====================
        // 这里是在线程启动之后,线程结果返回之前
        System.out.println("这里可以为所欲为....");
        // ====================

        // 为所欲为完毕之后,拿到线程的执行结果
        Integer result = task.get();
        System.out.println("主线程中拿到异步任务执行的结果为:" + result);
    }
}
Nach dem Login kopieren

执行结果如下:

这里可以为所欲为....
线程任务开始执行了....
主线程中拿到异步任务执行的结果为:1
Nach dem Login kopieren

Callable中可以通过范型参数来指定线程的返回值类型。通过FutureTask的get方法拿到线程的返回值。

第六种方式:基于线程池的方式

我们知道,线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。当然了,线程池也不需要我们来实现,jdk的官方也给我们提供了API。

代码如下:

package com.kingh.thread.create;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池
 *
 * @author <a href="https://blog.csdn.net/king_kgh>Kingh</a>
 * @version 1.0
 * @date 2019/3/18 10:04
 */
public class CreateThreadDemo12_ThreadPool {

    public static void main(String[] args) throws Exception {

        // 创建固定大小的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);

        while (true) {
            // 提交多个线程任务,并执行
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    printThreadInfo();
                }
            });
        }
    }

    /**
     * 输出当前线程的信息
     */
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}
Nach dem Login kopieren

执行结果如下:

当前运行的线程名为: pool-1-thread-1
当前运行的线程名为: pool-1-thread-2
当前运行的线程名为: pool-1-thread-4
当前运行的线程名为: pool-1-thread-3
当前运行的线程名为: pool-1-thread-7
当前运行的线程名为: pool-1-thread-8
当前运行的线程名为: pool-1-thread-9
当前运行的线程名为: pool-1-thread-6
当前运行的线程名为: pool-1-thread-5
当前运行的线程名为: pool-1-thread-10
Nach dem Login kopieren

线程池的内容还有非常多,这里不再详细地讲解。

更多编程相关知识,请访问:编程教学!!

Das obige ist der detaillierte Inhalt vonMehrere Möglichkeiten, Multithreading in Java zu implementieren. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage