线程及创建线程的三种方法

线程及创建线程的三种方法

什么是线程

世间万物都可以同时完成很多工作。例如,人体可以同时进行呼吸、血液循环、思考问题等活动。用户既可以使用计算机听歌,也可以编写文档和发送邮件,而这些活动的完成可以同时进行。这种同时执行多个操作的“思想”在 Java 中被称为并发,而将并发完成的每一件事称为线程。

在 Java 中,并发机制非常重要,但并不是所有程序语言都支持线程。在以往的程序中,多以一个任务完成以后再进行下一个任务的模式进行,这样下一个任务的开始必须等待前一个任务的结束。Java 语言提供了并发机制,允许开发人员在程序中执行多个线程,每个线程完成一个功能,并与其他线程并发执行。这种机制被称为多线程。

多线程是非常复杂的机制,比如同时阅读 3 本书。首先阅读第 1 本第 1 章,然后再阅读第 2 本第 1 章,再阅读第 3 本第 1 章,接着回过头阅读第 1 本第 2 章,以此类推,就体现了多线程的复杂性。

既然多线程这么复杂,那么它在操作系统中是怎样工作的呢?其实,Java 中的多线程在每个操作系统中的运行方式也存在差异,在此以 Windows 操作系统为例介绍其运行模式。

Windows 系统是多任务操作系统,它以进程为单位。一个进程是一个包含有自身地址的程序,每个独立执行的程序都称为进程,也就是正在执行的程序。图 1 所示为 Windows 7 系统下使用任务管理器查看进程的结果。

 

img 图1 查看 Windows 7 的进程(点此查看原图

系统可以分配给每个进程一段有限的执行 CPU 的时间(也称为 CPU 时间片),CPU 在这段时间中执行某个进程,然后下一个时间段又跳到另一个进程中去执行。由于 CPU 切换的速度非常快,给使用者的感受就是这些任务似乎在同时运行,所以使用多线程技术后,可以在同一时间内运行更多不同种类的任务。

图 2 的左图是单线程环境下任务 1 和任务 2 的执行模式。任务 1 和任务 2 是两个完全独立、互不相关的任务,任务 1 是在等待远程服务器返回数据,以便进行后期的处理,这时 CPU 一直处于等待状态,一直在“空运行”。如果任务 2 是在 5 秒之后被运行,虽然执行任务 2 用的时间非常短,仅仅是 1 秒,但必须在任务1运行结束后才可以运行任务 2。由于运行在单任务环境中,所以任务 2 有非常长的等待时间,系统运行效率大幅降低。

单任务的特点就是排队执行,也就是同步,就像在 cmd 中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即 CPU 利用率大幅降低。

 

img 图2 单线程和多线程执行模式

图 2 的右侧则是多线程环境下的执行模式。从中可以发现,CPU 完全可以在任务 1 和任务 2 之间来回切换,使任务 2 不必等到 5 秒再运行,系统的运行效率大大得到提升。这就是要使用多线程技术、要学习多线程的原因。

那么什么是线程呢?线程可以理解成是在进程中独立运行的子任务。比如,QQ.exe 运行时就有很多的子任务在同时运行。像好友视频、下载文件、传输数据、发送表情等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等功能都有对应的线程在后台默默地运行。

创建线程的方式总结

继承Thread类

  1. 子类继承Thread类具备多线程能力

  2. 启动线程:子类对象.start()

  3. 不建议使用:避免OOP单继承局限性

实现Runnable接口

  1. 实现接口Runnable具有多线程能力

  2. 启动线程:传入目标对象+Thread对象.start()

  3. 推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用

Thread类创建多线程

package com.xxgc.demo01;

//创建线程方式一:继承Thread类,重写run方法。调用start开启线程
public class TestThread extends Thread {
   @Override
   public void run() {
       //run方法线程体
       for (int i = 0; i < 100; i++) {
           System.out.println("我在看代码--"+i);
      }
  }

   public static void main(String[] args) {
       //创建一个线程对象
       TestThread t1 = new TestThread();
       //调用start()方法开启线程
       t1.start();
       //main线程,主线程
       for (int i = 0; i < 1000; i++) {
           System.out.println("我在学习多线程--"+i);
      }
  }
}

Runnable接口实现多线程

package com.xxgc.demo01;

//创建线程方式2:实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start()方法
public class TestThread3 implements Runnable{
   @Override
   public void run() {
       //run方法线程体
       for (int i = 0; i < 100; i++) {
           System.out.println("我在看代码--"+i);
      }
  }

   public static void main(String[] args) {
       //创建一个线程对象
       TestThread t1 = new TestThread();

       //创建线程对象,通过线程对象来开启我们的线程,代理
       //Thread thread = new Thread(t1);
       //thread.start();

       //简化代码
       new Thread(t1).start();
       //main线程,主线程
       for (int i = 0; i < 1000; i++) {
           System.out.println("我在学习多线程--"+i);
      }
  }
}

实现callable接口

package com.xxgc.demo02;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//线程创建方式三:实现callable接口
/*
callable的好处:
1.可以定义返回值
2.可以抛出异常
*/
public class TestCallable implements Callable<Boolean> {
   private String url;  //网络图片地址
   private String name; //保存的文件名

   public TestCallable(String url,String name){
       this.url = url;
       this.name = name;
  }
   @Override
   public Boolean call(){
       WebDownloader webDownloader = new WebDownloader();
       webDownloader.downloader(url,name);
       System.out.println("下载了文件名为:"+name);
       return true;
  }
   public static void main(String[] args) throws ExecutionException, InterruptedException {
       TestCallable t1 = new TestCallable("https://bkimg.cdn.bcebos.com/pic/9f2f070828381f30e924d00f30485b086e061d955a49?x-bce-process=image/resize,m_lfit,w_268,limit_1/format,f_jpg","1.jpg");
       TestCallable t2 = new TestCallable("https://bkimg.cdn.bcebos.com/pic/d52a2834349b033b5bb5fc1e128721d3d539b7001794?x-bce-process=image/resize,m_fill,w_82,h_102,align_50,limit_0","2.jpg");
       TestCallable t3 = new TestCallable("https://bkimg.cdn.bcebos.com/pic/cc11728b4710b912c8fc1c7313b5eb039245d78884af?x-bce-process=image/resize,m_fill,w_82,h_102,align_50,limit_0","3.jpg");
       TestCallable t4 = new TestCallable("https://bkimg.cdn.bcebos.com/pic/c8ea15ce36d3d539b60028c915cefe50352ac65c1a2a?x-bce-process=image/resize,m_lfit,h_168,limit_1","4.jpg");
       TestCallable t5 = new TestCallable("https://bkimg.cdn.bcebos.com/pic/359b033b5bb5c9ea15ce39e9fa70a1003af33a87152a?x-bce-process=image/resize,m_lfit,h_168,limit_1","5.jpg");

       //创建执行服务
       ExecutorService ser = Executors.newFixedThreadPool(3);
       //提交执行
       Future<Boolean> r1 = ser.submit(t1);
       Future<Boolean> r2 = ser.submit(t2);
       Future<Boolean> r3 = ser.submit(t3);
       Future<Boolean> r4 = ser.submit(t4);
       Future<Boolean> r5 = ser.submit(t5);
       //获取结果
       boolean rs1 = r1.get();
       boolean rs2 = r2.get();
       boolean rs3 = r3.get();
       boolean rs4 = r4.get();
       boolean rs5 = r5.get();

       System.out.println(rs1);
       System.out.println(rs2);
       System.out.println(rs3);
       System.out.println(rs4);
       System.out.println(rs5);
       //关闭服务
       ser.shutdownNow();
  }
}
//下载器
class WebDownloader {
   //下载方法
   public void downloader(String url, String name) {
       try {
           FileUtils.copyURLToFile(new URL(url), new File(name));
      } catch (IOException e) {
           e.printStackTrace();
           System.out.println("IO异常,downloader方法出现问题");
      }
  }
}

 

posted @ 2020-09-18 17:28  墨染念颖  阅读(577)  评论(0)    收藏  举报