zoukankan      html  css  js  c++  java
  • 线程的四种创建方式

    线程的创建方式

    1. 继承Thread类

    • 编写线程类继承Thread类,重写run方法

      public class MyThread extends Thread{
          //重写Thread中的run方法,在这里面写该线程的执行代码
          public void run() {
              for (int i =0;i<10;i++){
                  System.out.println("线程执行了:"+new Date().getTime());
              }
          }
      }
      
    • 主程序中启动线程

      public class Main {
          public static void main(String[] args) {
              //1.创建自定义线程实例
              MyThread myThread = new MyThread();
              //2.启动线程
              myThread.start();
              //3.在Main主线程中打印信息
              for (int i =0;i<10;i++){
                  System.out.println("Main主线程执行了:"+new Date().getTime());
              }
          }
      }
      
      • 结果:两个线程并发进行,同一时间也有先后顺序

        Main主线程执行了:1591682458017
        线程执行了:1591682458017
        Main主线程执行了:1591682458017
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        线程执行了:1591682458018
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        Main主线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        Main主线程执行了:1591682458018
        线程执行了:1591682458018
        

    2.实现Runnable接口

    第一种方法

    • 实现Runnable接口中的run方法,编写线程工作代码

      public class MyRunnable implements Runnable{
          public void run() {
              for (int i =0;i<5;i++){
                  System.out.println(Thread.currentThread().getName()+"线程执行了:"+new Date().getTime());
              }
          }
      }
      
    • 将其作为参数传入Thread实例对象启动线程

              //1.在Main主线程中打印信息
              for (int i =0;i<10;i++){
                  System.out.println(Thread.currentThread().getName()+"主线程执行了:"+new Date().getTime());
              }
              //2.将我们的Runnable实现类作为参数传入Tread实例对象,第二个参数可以指定线程名
              Thread thread = new Thread(new MyRunnable(),"MyRunnable");
              thread.start();
      

      第二种方法

      直接在创建Thread实例的时候实现Runnable

      Thread thread1 = new Thread(new Runnable() {
          public void run() {
              for (int i =0;i<5;i++){
                  System.out.println(Thread.currentThread().getName()+"线程执行了:"+new Date().getTime());
              }
          }
      },"MyRunnable2");
      thread1.start();
      

      小结

      • Runnable的方式运行比继承Thread的方法要慢

    3.实现callable接口

    1. FutureTask

    callable接口需要FutureTask帮助执行,FutureTask的结构图如下

    • Runnable是创建线程的,callable被调用最终也是调用了Runnable,即callable依赖Runnable
    • Future是帮助callable在执行过程获取一些参数的返回值、中止callable接口的执行
    • 常用的Future接口方法
      • 判断任务是否完成:idDone()
      • 中断任务:cancel()
      • 获取任务执行结果:get()

    2.演示实现

    • 实现callable接口中的call方法,定义返回值

      public class MyCallable implements Callable<String> {
          //相当于run方法,只是多了个返回值
          public String call() throws Exception {
              for (int i =0;i<5;i++){
                  System.out.println(Thread.currentThread().getName()+"线程执行了:"+new Date().getTime());
              }
              return "Callable线程执行完成";
          }
      }
      
    • 在主程序里创建FutureTask对象,用其来创建Thread实例,启动线程

              //1.利用Callable创建FutureTask实例对象,泛型类型为Callable中call方法的返回值类型
              FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
              //2.创建线程实例
              Thread thread = new Thread(futureTask,"MyCallable");
              //3.启动线程
              thread.start();
              //4.获取返回值信息
              try {
                  String s = futureTask.get();
                  System.out.println(s);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              //4.在Main主线程中打印信息
              for (int i =0;i<5;i++){
                  System.out.println(Thread.currentThread().getName()+"主线程执行了:"+new Date().getTime());
              }
      

    4.线程池(开发常用)

    1.线程池的类间关系

    • Executor:线程执行代码的执行器,声明了execute(Runnable runnable)方法,执行任务代码
    • ScheduledExecutorService:定义了与定时任务相关的服务
    • ScheduledThreadPoolExecutor:定时任务执行的线程池
    • ThreadPoolExecutor:通过这个来获取线程池比较麻烦,需要设置很多的参数
    • Executors:是一个工具类,它帮助我们更方便的创建四大线程池,简化了参数设置
    • 四大线程池
      • CacheThreadPool
      • FixedThreadPool
      • ScheduledThreadPool
      • SingleThreadPool

    2.线程池创建

    这里只简单介绍一种 FixedThreadPool,四种常用线程池链接入口:https://www.cnblogs.com/JIATCODE/p/13276283.html

    • 使用Executors获取线程池对象,得到的是一个ExecutorService,该对象中就要有父类接口的execute方法,可以启动线程

              //1.使用Executors来获取ExecutorService线程池对象,newFixedThreadPool参数指定最大线程数
              ExecutorService executorService = Executors.newFixedThreadPool(10);
              //2.使用线程池对象传入Runnable(执行代码),调用execute方法就可以启动线程
              executorService.execute(new MyRunnable());
      
              //3.设置主线程
              for (int i =0;i<5;i++){
                  System.out.println(Thread.currentThread().getName()+"主线程执行了:"+new Date().getTime());
              }
      

    3.简单分析Executors创建原理

    ThreadPoolExecutor是创建线程池的类,但是由于设置的参数过于繁琐,不利于开发,所以Executors可以辅助创建线程池,大致的方式是Executors在内部把不经常改动的参数先预先设置好,只留下几个常需要自定义的参数设置接口,给用户来定义,以达到简便创建的目的

    源码分析

    • 创建线程池

       ExecutorService executorService = Executors.newFixedThreadPool(10);
      
    • 进入到Executors中,newFixedThreadPool()方法如下:

      public static ExecutorService newFixedThreadPool(int nThreads) {
          //真正实例化ThreadPoolExecutor对象,遵守依赖倒置原则返回接口ExecutorService,ThreadPoolExecutor为其实现类的子类
          //创建ThreadPoolExecutor所需要的参数在Executors的对应创建方法中已经定义了,而只留下线程池的最大线程数给用户自定义
          return new ThreadPoolExecutor(nThreads, nThreads,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>());
      }
      
    • 观察ThreadPoolExecutor的构造函数,四大线程池对应的是ThreadPoolExecutor中的四种参数设置模式,ThreadPoolExecutor类中有四个构造方法,各自对应

      //    FixedThreadPool类型线程池对应的构造函数
      public ThreadPoolExecutor(int corePoolSize,
                                    int maximumPoolSize,
                                    long keepAliveTime,
                                    TimeUnit unit,
                                    BlockingQueue<Runnable> workQueue) {
              this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                   Executors.defaultThreadFactory(), defaultHandler);
          }
      

    从繁琐的参数到用户创建只有一个参数,这就是Executors的作用

    线程创建小结

    • 实现接口方式的好处
      • 接口更适合多个相同的程序代码去共享同一个资源
      • 接口可以弥补java的单继承的局限性
      • 接口代码可以被多个线程共享,代码和线程独立
      • 线程池只能实现Runnable或者Callable接口的线程
    • JAVA中,每次程序启动至少有两个线程,主程序线程和垃圾收集线程
    • Runnable和Callable接口的比较
      • 都可以编写多线程程序,都需要调用start()来启动线程
      • Callable接口可以获取线程运行的信息以及中止线程,Runnable只能提供基本的线程运行工作,Callable的功能更丰富一些
      • Callable的call()方法允许抛出异常,Runnable的run()方法则不允许
      • 当使用FutureTask.get()方法时,主线程会阻塞,因为该方法返回的是该线程的运行结果,只有等到该线程结束才可以返回结果,而该方法写在主线程中,主线程会因为该方法等待线程结束而阻塞,直到返回出了运行结果,主程序才会继续运行,所以FutureTask.get()要在不需要并发的时候去调用
  • 相关阅读:
    POJ
    HDU
    Python之列表
    列表、元组、字典总结
    Python之列表、原组、字典总结
    [P1082][NOIP2012] 同余方程 (扩展欧几里得/乘法逆元)
    [P3957][NOIP2017]跳房子 (DP+二分/队列?)
    [Codeforces896C] Willem, Chtholly and Seniorious (ODT-珂朵莉树)
    [P1005][NOIP2007] 矩阵取数游戏 (DP+高精)
    [POJ1006]生理周期 (中国剩余定理)
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13276263.html
Copyright © 2011-2022 走看看