マルチスレッドを実装する方法は1つ、2つ、または4つありますか?

picture.png

一緒に書く習慣をつけましょう!「ナゲッツデイリーニュープラン・4月アップデートチャレンジ」に参加して3日目です。クリックしてイベントの詳細をご覧ください

1.オラクルの公式ウェブサイトのドキュメントはどのように書かれていますか?

  • 方法1:Runnableインターフェースを実装する
  • 方法2:Threadクラスを継承する

Runnableインターフェースを実装する

package threadcoreknowledge.createthreads;

/**
 * 描述: 用Runnable方式创建线程
 */
public class RunnableStyle implements Runnable{
    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableStyle());
        thread.start();
    }
    public void run() {
        System.out.println("用Runnable方式创建线程");
    }
}
复制代码

Threadクラスから継承します

package threadcoreknowledge.createthreads;

/**
 * 描述:  用Thread方式实现线程
 */
public class ThreadStyle extends Thread{
    @Override
    public void run() {
        System.out.println("用Thread方式实现线程");
    }

    public static void main(String[] args) {
        new ThreadStyle().start(); 
    }
}
复制代码

2.2つの方法の比較

方法1(Runnableインターフェースの実装)の方が優れています。

方法2のデメリット:

  • コードのアーキテクチャを考慮すると、実行する特定のタスク(runメソッドの内容)をスレッドから切り離す必要があります。これら2つのことを混同しないでください。
  • リソースの節約という点では、Threadクラスが継承されます。新しいタスクを作成するたびに、新しい独立したスレッドしか作成できず、新しい独立したスレッドの消費量は非常に大きくなります(作成する必要があるため、破壊されました);
  • Threadクラスを継承すると、Javaは二重継承をサポートしないため、このクラスは他のクラスを継承できず、コードのスケーラビリティが大幅に制限されます。

2つの方法の本質的な比較

  • 方法1:最後にtarget.run();を呼び出します。
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
复制代码
  • 方法2:run()は完全に書き直されます

3.質問:2つの方法を同時に使用するとどうなりますか?

package threadcoreknowledge.createthreads;

/**
 * 描述  同时使用Runnable和Thread两种实现线程的方式
 */
public class BothRunnableThread {
    public static void main(String[] args) {
        new Thread(new Runnable() {  //传入Runnable对象
            @Override
            public void run() {
                System.out.println("我来自Runnable");
            }
        }){
            @Override				//重写run()方法
            public void run() {      	
                System.out.println("我来自Thread");
            }
        }.start();
    }
}
复制代码

コード実行結果:

オブジェクト指向の考え方から考えてみましょう。run()メソッドを書き直したため、Threadのrun()メソッドコードの3行は存在しなくなり、Runnableオブジェクトが渡されても実行されなくなります。

4.要約:最も正確な説明

1.通常、私たちは2つのカテゴリーに分けることができますが、Oracleも同じことを言っています

2.正確には、スレッドを作成する方法は1つ、つまりThreadクラスを作成する方法と、スレッドの実行ユニットを実装する方法は2つあります。

  • 方法1:Runnableインターフェースのrunメソッドを実装し、RunnableインスタンスをThreadクラスに渡します。
  • 方法2:Threadのrunメソッドをオーバーライドします(Threadクラスを継承します)。

5.典型的な誤解の分析

1.「スレッドプールにスレッドを作成することは、新しいスレッドを作成する方法でもあります」

package threadcoreknowledge.wrongways;

import javafx.concurrent.Task;

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

public class ThreadPools {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            executorService.submit(new Tasks() {
            });
        }
    }
}
class Tasks implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}
复制代码

コード実行結果:

public Thread newThread(Runnable r) {
	Thread t = new Thread(group, r,
		 namePrefix + threadNumber.getAndIncrement(),
						  0);
	if (t.isDaemon())
		t.setDaemon(false);
	if (t.getPriority() != Thread.NORM_PRIORITY)
		t.setPriority(Thread.NORM_PRIORITY);
	return t;
}
复制代码

ソースコードをクリックすると、スレッドプールが本質的にスレッドを作成することがわかりますnew Thread。したがって、これはスレッドを作成する新しい方法ではありません。

2.「CallableとFutureTaskを介してスレッドを作成することも、新しいスレッドを作成する方法です」

本質は、Runnableインターフェースを実装し、Threadクラスを継承することです。

3.「実行可能なインターフェースを実装するための戻り値はなく、呼び出し可能なインターフェースを実装するための戻り値があるため、callableはスレッドを実装するための新しい方法です。」

本質は、Runnableインターフェースを実装し、Threadクラスを継承することによって引き続き実装されます。

4.タイマー

package threadcoreknowledge.wrongways;

import java.util.Timer;
import java.util.TimerTask;

/**
 * 描述:      定时器创建线程
 */
public class DemoTimerTask {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        },1000,1000);
    }
}
复制代码

コード実行結果:

5.匿名の内部クラス

package threadcoreknowledge.wrongways;

public class AnonymousInnerClassDemo {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }).start();
    }
}
复制代码

コード実行結果:

本質的に同じです。

6.ラムダ式

package threadcoreknowledge.wrongways;

public class Lambda {
    public static void main(String[] args) {
        new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
    }
}
复制代码

コード実行結果:

本質的に同じです。

6.典型的な誤解の要約

マルチスレッドの実装はコード内で絶えず変化していますが、その本質は同じままです。彼らはスレッドプール、タイマーなどのさまざまなパッケージを使用しており、パッケージの外観はスレッドプールを実現する方法のようですが、パッケージを開いてソースコードを調べ、彼の最終的な実現の真実を確認すると、本質は、実際にはRunnableインターフェースと継承Threadクラスの実現です。

7.マルチスレッドの実装-一般的なインタビューの質問

スレッドを実装する方法はいくつありますか?5つのアイデアがあります:

  1. さまざまな観点から、さまざまな答えがあります。
  2. 典型的な答えは2つです
  3. 原理を見てみましょう、両方の本質は同じです
  4. 具体的には、他の方法について話しましょう
  5. 結論は

Runnableインターフェースを実装するか、Threadクラスから継承するのにどちらの方法が良いですか?

  1. コードアーキテクチャの観点から
  2. 新しいスレッドの喪失
  3. Javaは二重継承をサポートしていません

おすすめ

転載: juejin.im/post/7082722150091587621