目次
1. 複数のスレッドを作成する上記の例では、作成した 1 つのスレッドに加えて、実際にはメインスレッドも実行中。では、これら 2 つのスレッド以外に実行中のスレッドはありますか? 実際には存在します。たとえば、見えない " >1. 複数のスレッドを作成する上記の例では、作成した 1 つのスレッドに加えて、実際にはメインスレッドも実行中。では、これら 2 つのスレッド以外に実行中のスレッドはありますか? 実際には存在します。たとえば、見えない
2. スレッド名を指定してください複数の Thread クラスを作成し、その start メソッドを呼び出して複数のスレッドを開始することでそれを確認できます。各スレッドには独自の名前があり、上記のコードでは、作成されたスレッドに MyThread-01 と MyThread-02 という名前が割り当てられ、親クラスの setName メソッドを呼び出してスレッド名が割り当てられます。コンストラクターメソッド。スレッド名を指定しない場合、システムはデフォルトでスレッド名を指定し、命名規則は Thread-N の形式になります。ただし、トラブルシューティングを容易にするために、スレッドを作成するときに適切なスレッド名を指定することをお勧めします。次のコードは、スレッド名を使用しない場合のコードです。" >2. スレッド名を指定してください複数の Thread クラスを作成し、その start メソッドを呼び出して複数のスレッドを開始することでそれを確認できます。各スレッドには独自の名前があり、上記のコードでは、作成されたスレッドに MyThread-01 と MyThread-02 という名前が割り当てられ、親クラスの setName メソッドを呼び出してスレッド名が割り当てられます。コンストラクターメソッド。スレッド名を指定しない場合、システムはデフォルトでスレッド名を指定し、命名規則は Thread-N の形式になります。ただし、トラブルシューティングを容易にするために、スレッドを作成するときに適切なスレッド名を指定することをお勧めします。次のコードは、スレッド名を使用しない場合のコードです。
1. 创建线程任务" >1. 创建线程任务
2. 创建可运行类" >2. 创建可运行类
3. lambda方式创建线程任务" >3. lambda方式创建线程任务
lambda 方式改造" >lambda 方式改造
1. 指定时间点执行" >1. 指定时间点执行
2.间隔时间重复执行" >2.间隔时间重复执行
ホームページ Java &#&ベース Javaでマルチスレッドを実装するいくつかの方法

Javaでマルチスレッドを実装するいくつかの方法

Jan 04, 2023 pm 03:52 PM
java マルチスレッド化

マルチスレッドを実装する方法: 1. Thread クラスを継承し、JDK が提供する Thread クラスを使用し、Thread クラスの run メソッドを書き換えます。 2. Runnable インターフェイスを実装します。 FunctionalInterface」関数式インターフェイス。これは、JDK8 が提供するラムダ メソッドを使用してスレッド タスクを作成できることを意味します。3. 内部クラスを使用します。4. タイマーを使用します。5. 戻り値を含むスレッドの実装。6. スレッドに基づいて複数のスレッドを実装します。プールのスレッド。

Javaでマルチスレッドを実装するいくつかの方法

#このチュートリアルの動作環境: Windows7 システム、Java8 バージョン、DELL G3 コンピューター。

フォームでマルチスレッドを実装するには主に 2 つの方法があります。1 つは Thread クラスを継承する方法、もう 1 つは Runnable インターフェイスを実装する方法です。基本的に、実装方法は、スレッド タスクを実装し、スレッドを開始してスレッド タスクを実行することです (ここでのスレッド タスクは実際には run メソッドです)。ここで挙げた6種類は、実は上記2種類をベースに一部改良を加えたものです。

以下では、これら 6 つの実装方法を 1 つずつ紹介します。

最初の方法: Thread クラスを継承する

すべてがオブジェクトであり、スレッドもオブジェクトであり、オブジェクトはそのパブリック特性を抽出してクラスにカプセル化することができ、クラスを使用して複数のオブジェクトをインスタンス化することができます。その場合、スレッドを実装する最初の方法は、

Thread クラスを継承することです。 Threadクラスを継承するのが最も簡単なスレッド実装方法で、ThreadクラスのrunメソッドをJDKが提供するThreadクラスに書き換えるだけで、スレッド起動時にrunメソッド本体の内容が実行されます。コードは次のとおりです。

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());
    }
}
ログイン後にコピー
実行結果は次のとおりです。

当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: main
当前运行的线程名为: MyThread
当前运行的线程名为: MyThread
当前运行的线程名为: main
ログイン後にコピー

ここで、スレッドの開始時にスレッド クラスの run メソッドが呼び出されないことに注意してください。 、しかし、スレッドクラスの開始は.メソッドが呼び出されます。それでは、 run メソッドを呼び出すことができますか?答えは「はい」です。run メソッドはパブリックに宣言されたメソッドなので呼び出すことができますが、run メソッドを呼び出すと、このメソッドは通常のメソッドとして呼び出され、スレッドは開始されません。これは実際にデザインパターンのテンプレートメソッドパターンを利用しており、Threadクラスがテンプレートとなり、runメソッドが変更されるためサブクラスで実装しています。

1. 複数のスレッドを作成する上記の例では、作成した 1 つのスレッドに加えて、実際にはメインスレッドも実行中。では、これら 2 つのスレッド以外に実行中のスレッドはありますか? 実際には存在します。たとえば、見えない

ガベージ コレクション スレッド

もサイレントで実行されています。ここでは、実行中のスレッドの数は考慮しません。上で自分でスレッドを作成したので、さらにいくつかのスレッドを作成して一緒に実行できますか? 答えは「はい」です。 Thread クラスはスレッド オブジェクトであるため、複数の Thread クラスを作成し、それらの start メソッドを呼び出して複数のスレッドを開始します。コードは次のとおりです

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());
    }
}
ログイン後にコピー
実行結果は次のとおりです

当前运行的线程名为: main
当前运行的线程名为: MyThread-02
当前运行的线程名为: MyThread-01
当前运行的线程名为: main
当前运行的线程名为: MyThread-01
当前运行的线程名为: MyThread-02
当前运行的线程名为: main
ログイン後にコピー

2. スレッド名を指定してください複数の Thread クラスを作成し、その start メソッドを呼び出して複数のスレッドを開始することでそれを確認できます。各スレッドには独自の名前があり、上記のコードでは、作成されたスレッドに MyThread-01 と MyThread-02 という名前が割り当てられ、親クラスの setName メソッドを呼び出してスレッド名が割り当てられます。コンストラクターメソッド。スレッド名を指定しない場合、システムはデフォルトでスレッド名を指定し、命名規則は Thread-N の形式になります。ただし、トラブルシューティングを容易にするために、スレッドを作成するときに適切なスレッド名を指定することをお勧めします。次のコードは、スレッド名を使用しない場合のコードです。

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());
    }
}
ログイン後にコピー

実行結果は次のとおりです。

当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0
当前运行的线程名为: main
当前运行的线程名为: Thread-1
当前运行的线程名为: Thread-0
ログイン後にコピー

2 番目の方法: Runnable インターフェイスの実装##Runnable インターフェイスの実装は、スレッドを作成する一般的な方法でもあり、インターフェイスを使用すると、プログラムの結合を減らすことができます。 Runnable インターフェイスには、実行されるメソッドが 1 つだけ定義されています。 Runnable インターフェイスのコードを見てみましょう。

package java.lang;

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
ログイン後にコピー

実際、Runnable はスレッド タスクです。スレッド タスクとスレッド コントロールは分離されています。これが上記の分離です。

スレッドを実装したい場合は、Thread クラスを使用できます。Thread クラスによって実行されるタスクは、Runnable インターフェイスを実装したクラスによって処理できます。

これが Runnable の本質です。

Runnable は @FunctionalInterface 関数インターフェイスであり、JDK8 が提供するラムダ メソッドを使用してスレッド タスクを作成できることを意味します。次のコードは、その使用方法を読者に示します。

Runnable を使用して上記の例を実装する手順は次のとおりです:

  • 定义一个类实现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());
    }
}
ログイン後にコピー

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());
    }
}
ログイン後にコピー

线程任务和线程的控制分离,那么一个线程任务可以提交给多个线程来执行。这是很有用的,比如车站的售票窗口,每个窗口可以看做是一个线程,他们每个窗口做的事情都是一样的,也就是售票。这样我们程序在模拟现实的时候就可以定义一个售票任务,让多个窗口同时执行这一个任务。那么如果要改动任务执行计划,只要修改线程任务类,所有的线程就都会按照修改后的来执行。相比较继承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);
        }
    }
}
ログイン後にコピー

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

这并不是一种新的实现线程的方式,只是另外的一种写法。比如有些情况我们的线程就想执行一次,以后就用不到了。那么像上面两种方式(继承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);
        }

    }
}
ログイン後にコピー

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

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);
        }

    }
}
ログイン後にコピー

运行结果如下:

sub class
sub class
ログイン後にコピー

我们可以看到,其实是基于子类的执行了,为什么呢,其实很简单,我们先来看一下为什么不基于子类的时候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();
}
ログイン後にコピー

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

@Override
public void run() {
    if (target != null) {
        target.run();
    }
}
ログイン後にコピー

我们可以看到,如果通过上面的构造方法传入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();
ログイン後にコピー

第四种方式:定时器

定时器可以说是一种基于线程的一个工具类,可以定时的来执行某个任务。在应用中经常需要定期执行一些操作,比如要在凌晨的时候汇总一些数据,比如要每隔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"));
    }
}
ログイン後にコピー

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);
    }
}
ログイン後にコピー

关于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);
    }
}
ログイン後にコピー

执行结果如下:

这里可以为所欲为....
线程任务开始执行了....
主线程中拿到异步任务执行的结果为:1
ログイン後にコピー

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);
        }

    }
}
ログイン後にコピー

执行结果如下:

当前运行的线程名为: 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
ログイン後にコピー

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

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

以上がJavaでマルチスレッドを実装するいくつかの方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

Java での日付までのタイムスタンプ Java での日付までのタイムスタンプ Aug 30, 2024 pm 04:28 PM

Java での日付までのタイムスタンプに関するガイド。ここでは、Java でタイムスタンプを日付に変換する方法とその概要について、例とともに説明します。

カプセルの量を見つけるためのJavaプログラム カプセルの量を見つけるためのJavaプログラム Feb 07, 2025 am 11:37 AM

カプセルは3次元の幾何学的図形で、両端にシリンダーと半球で構成されています。カプセルの体積は、シリンダーの体積と両端に半球の体積を追加することで計算できます。このチュートリアルでは、さまざまな方法を使用して、Javaの特定のカプセルの体積を計算する方法について説明します。 カプセルボリュームフォーミュラ カプセルボリュームの式は次のとおりです。 カプセル体積=円筒形の体積2つの半球体積 で、 R:半球の半径。 H:シリンダーの高さ(半球を除く)。 例1 入力 RADIUS = 5ユニット 高さ= 10単位 出力 ボリューム= 1570.8立方ユニット 説明する 式を使用してボリュームを計算します。 ボリューム=π×R2×H(4

未来を創る: まったくの初心者のための Java プログラミング 未来を創る: まったくの初心者のための Java プログラミング Oct 13, 2024 pm 01:32 PM

Java は、初心者と経験豊富な開発者の両方が学習できる人気のあるプログラミング言語です。このチュートリアルは基本的な概念から始まり、高度なトピックに進みます。 Java Development Kit をインストールしたら、簡単な「Hello, World!」プログラムを作成してプログラミングを練習できます。コードを理解したら、コマンド プロンプトを使用してプログラムをコンパイルして実行すると、コンソールに「Hello, World!」と出力されます。 Java の学習はプログラミングの旅の始まりであり、習熟が深まるにつれて、より複雑なアプリケーションを作成できるようになります。

See all articles