线程通常可以通过三种方式来创建,第一种是继承Thread类,第二中是实现Runnable接口,第三种则是实现Callable接口,其中第二种方式是最重要的,而且Thread类也是实现的Runnable接口。
image.png

继承Thread类

首先自定义线程类继承Thread类,然后重写Run方法,在Run方法中编写线程执行体,最后就可以创建线程对象,调用start方法启动线程。先看一个使用多线程下载三张图片的例子:

  1. import org.apache.commons.io.FileUtils;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.net.URL;
  5. /**
  6. * @author wjh
  7. * @date 2021/7/15 10:37
  8. * @Package PACKAGE_NAME
  9. */
  10. public class MyThread extends Thread {
  11. private String url;
  12. private String name;
  13. public MyThread(String url, String name) {
  14. this.url = url;
  15. this.name = name;
  16. }
  17. @Override
  18. public void run() {
  19. WebDownloader webDownloader = new WebDownloader();
  20. webDownloader.downloader(url, name);
  21. System.out.println("下载了文件名为:" + name);
  22. }
  23. public static void main(String[] args) {
  24. MyThread myThread1 = new MyThread("https://static.runoob.com/images/demo/demo2.jpg", "1.jpg");
  25. MyThread myThread2 = new MyThread("http://img.desktx.com/d/file/wallpaper/scenery/20170107/080145c3a7460e7fa0369052a11467db.jpg", "2.jpg");
  26. MyThread myThread3 = new MyThread("http://img.ewebweb.com/uploads/20190623/21/1561296521-HiSnYbhyeE.jpg", "3.jpg");
  27. myThread1.start();
  28. myThread2.start();
  29. myThread3.start();
  30. }
  31. }
  32. class WebDownloader {
  33. public void downloader(String url, String name) {
  34. try {
  35. FileUtils.copyURLToFile(new URL(url), new File(name));
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. System.out.println("IO异常,downloader方法出现问题");
  39. }
  40. }
  41. }

运行结果:
image.png
可以看到下载顺序是321而不是按照123来下载,其实这三条线程不是按照代码的编写顺序执行的,而是同时执行的。

实现Runnable接口

首先自定义线程类实现Runnable接口,然后在Run方法中编写线程执行体,然后创建线程对象(Thread),而参数是实现Runnable接口的实例对象,然后使用Thread类的start方法启动线程。

  1. /**
  2. * @author wjh
  3. * @date 2021/7/15 10:56
  4. * @Package PACKAGE_NAME
  5. */
  6. public class TestThread implements Runnable{
  7. String title;
  8. public TestThread(String title){
  9. this.title=title;
  10. }
  11. @Override
  12. public void run() {
  13. for(int i=0;i<10;i++){
  14. System.out.println(title+":"+i);
  15. }
  16. }
  17. public static void main(String[] args) {
  18. TestThread testThread1=new TestThread("线程1");
  19. TestThread testThread2=new TestThread("线程2");
  20. new Thread(testThread1).start();
  21. new Thread(testThread2).start();
  22. }
  23. }

image.png

继承Thread类和实现Runnable接口的对比:
继承Thread:子类继承Thread类具有多线程能力,可以通过“子类对象.start()”来启动线程,但是由于Java单继承的机制限制,不建议使用该方法来创建线程。

实现Runnable接口:实现Runnable接口的类也具有多线程能力,可以通过“new Thread(实现接口的实例对象).start()”来启动线程,采用实现接口的方式避免了单继承的局限性,灵活方便,方便同一个对象被多个线程使用,建议使用这种方法来创建线程。

实现Callable接口

步骤:

  • 实现Callable接口,需要返回值类型;
  • 重写call方法,需要抛出异常;
  • 创建目标对象;
  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交执行:Future result1 = ser.submit(11);
  • 获取结果:boolean r1 = result1.get()
  • 关闭服务:ser.shutdownNow();

    1. /**
    2. * 实现Callable接口
    3. */
    4. public class Demo6_CreateCallable implements Callable<Boolean> {
    5. private String url;//网络图片地址
    6. private String name;//报错扥文件名
    7. //有参构造
    8. public Demo6_CreateCallable(String url, String name) {
    9. this.url = url;
    10. this.name = name;
    11. }
    12. //下载图片线程的执行体
    13. public Boolean call() throws Exception {
    14. WebDownloader webDownloader = new WebDownloader();
    15. webDownloader.downloader(url, name);
    16. System.out.println("下载了文件名为:" + name);
    17. return true;
    18. }
    19. public static void main(String[] args) throws ExecutionException, InterruptedException {
    20. Demo6_CreateCallable c = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "1.png");
    21. Demo6_CreateCallable c1 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "2.png");
    22. Demo6_CreateCallable c2 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "3.png");
    23. //创建执行服务
    24. ExecutorService ser = Executors.newFixedThreadPool(3);
    25. //提交执行
    26. Future<Boolean> r = ser.submit(c);
    27. Future<Boolean> r1 = ser.submit(c1);
    28. Future<Boolean> r2 = ser.submit(c2);
    29. //获取结果
    30. boolean res = r.get();
    31. boolean res1 = r1.get();
    32. boolean res2 = r2.get();
    33. //关闭服务
    34. ser.shutdownNow();
    35. }
    36. }