java实现/创建线程的几种方式小结

 更新时间:2021年08月09日 09:11:36   作者:勇敢牛牛不怕困难@帅  
在JAVA中,用Thread类代表线程,所有线程对象都必须是Thread类或者Thread类子类的实例,下面这篇文章主要介绍了java实现/创建线程的几种方式,需要的朋友可以参考下

进程与线程

进程可以简单理解成一个可执行程序例如.exe,在Windows中的任务管理器中可以查看每一个进程,进程是一次程序的执行,是程序在数据集合上运行的过程,是系统资源调度的一个单位。进程主要负责向操作系统申请资源。然而一个进程中,多个线程可以共享进程中相同的内存或文件资源。线程就是一个进程一个程序要完成所依赖的子任务,这些子任务便可以看作是一个线程。

第一种方式继承Thread类

从java源码可以看出Thread类本质上实现了Runnable接口的实例类,代表了线程的一个线程的实例,启动的线程唯一办法就是通过Thread类调用start()方法,start()方法是需要本地操作系统的支持,它将启动一个新的线程,并且执行run()方法。

继承Thread类实现线程代码如下

创建一个Thread类,对象直接调用run方法会出现什么问题?

package cn.thread.线程;

public class MyThread extends Thread{
    public MyThread(String name){
        super(null,null,name);
    }
    int piao =10;
    @Override
    public void run() {
        while(piao>0){
            System.out.println(Thread.currentThread().getName()+"......"+piao--);
        }
    }

    public static void main(String[] args) {
        MyThread mt = new MyThread("x");
        mt.run();
    }
}

结果:

可以发现是主线程执行了run方法,并不是用户线程执行的run方法,此时可以得出用户线程并没有启动,所以并不会执行run里面的方法,且执行完run方法便结束线程。

第二种创建线程的方法,实现Runnable接口

相比继承Thread类而言,实现接口的可扩展性得到了提升,Runnable接口也必须要封装到Thread类里面,才可以调用start方法,启动线程。

实现代码

package cn.thread.线程;

public class MyRunnable implements Runnable{

    int piao = 10;
    @Override
    public void run() {
        while(piao>0){
            System.out.println(Thread.currentThread().getName()+"-----"+piao--);
        }
    }

    public static void main(String[] args) {
        Runnable r =new MyRunnable();
        Thread t =new Thread(r);
        t.start();
    }

}

结果

第三种创建线程的方法实现Callable接口

Callable接口使用方法和Runnable接口的方法类似不同的一点是Callable接口具有返回值,返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

实现代码

对下列代码进行分析

首先callable是接口不能直接创建对象,也不能创建线程。并且要实现call方法类似run方法的功能,call方法有返回值,会计算结果,如果无法计算结果,则抛出一个异常。

执行callable任务之后,可以获得一个future的对象,future基本上是主线程可以跟踪进度以及获取其他线程结果的一种方式。在这里Test1()方法主要利用线程池和future的方法,去启动实现Callable接口的线程,具有返回值。而Test2()主要是采用FutureTask类去实现创建一个实现callable接口的线程,futuretask实现了future接口。

package com.openlab.test;

import java.util.Random;
import java.util.concurrent.Callable;

public class CallableTest implements Callable{

	@Override
	public Object call() throws Exception {
		
		Random generator = new Random();
		
		Integer randomNumber = generator.nextInt(5);
		
		Thread.sleep(randomNumber*1000);
		
		return randomNumber;
	}

}

综合练习代码:

package cn.thread.线程;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

public class CallableTest implements Callable<Object> {//不能直接创建线程
    int taskNum;
    public CallableTest(int taskNum){
        this.taskNum = taskNum;

    }
    @Override
    public Object call() throws Exception {

        System.out.println(">>>"+taskNum+"任务启动");
        Date dataTemp = new Date();
        Thread.sleep(1000);
        Date dataTemp2 = new Date();
        long time  = dataTemp2.getTime() - dataTemp.getTime();
        System.out.println(">>>>"+taskNum+"任务终止");
        return taskNum+"任务返回运行结果"+time;
//        Random generator = new Random();
//        Integer randomNumber = generator.nextInt(5);
//        Thread.sleep(randomNumber*1000);
//        return  randomNumber;
    }
    /*test1方法采用Executors的静态方法newFixedThreadPool(taskSize) 创建一个可重用固定线程集合的
    线程池,以共享的无界队列方式来运行这些线程,获取线程池。ExecutorService的submit方法提交一个
    callable实例,得到一个future对象,最终将future对象存储在list数组中,加入线程池的过程中就代表
    着线程已经开始执行,相当于一个线程池代理过程,就不需要采用start方法启动线程。最后对future进行
    打印输出。切记一定要关闭线程池!*/
     static void test1() throws ExecutionException, InterruptedException {
         System.out.println("程序开始");
         Date data1 = new Date();
         int taskSize = 5;
        //构建线程池对象
         ExecutorService pool = Executors.newFixedThreadPool(taskSize);
         List<Future> list =new ArrayList<Future>();
         for(int i=0;i<taskSize;i++){

             Callable c = new CallableTest(i);
             Future f = pool.submit(c);
             list.add(pool.submit(c));

         }
         //关闭线程池
         pool.shutdown();
         for(Future f:list){
             System.out.println(">>>"+f.get().toString());
         }
         Date date2 = new Date();
         System.out.println("程序运行结束-----"+(date2.getTime()-data1.getTime())+"毫秒");
     }
     /*test2方法主要是采用futuretask类,可以直接把callable作为参数来申明futuretask对象,
     这里相当于把线程池换成了futuretask数组,因为test1线程池可以对callable进行封装,
     在这里可以直接采用futuretask就行封装,在加上futuretask又实现了runnable接口,
     所以可以直接创建线程采用start的方式进行启动线程。*/
     static  void test2() throws ExecutionException, InterruptedException {
         System.out.println("----程序开始-----");
         Date date1 =new Date();
         int taskSize = 5;
         FutureTask[] randNumber = new FutureTask[taskSize];
         List<Future> list =new ArrayList<Future>();
         for(int i=0;i<taskSize;i++){
             Callable c = new CallableTest(i);
             randNumber[i] = new FutureTask(c);
             Thread t = new Thread(randNumber[i]);
             t.start();

         }
         for(Future f:randNumber){
             System.out.println(">>>"+f.get().toString());
         }
         Date date2 = new Date();
         System.out.println("程序运行结束-----"+(date2.getTime()-date1.getTime())+"毫秒");

     }
    public static void main(String[] args) throws Exception {
//        CallableTest c = new CallableTest();
//        Integer i = (Integer) c.call();
        test1();
        test2();

    }
}

执行结果

第四种实现线程的方法,基于线程池

其实在第三种的方法中就提到了两种实现方法,一种线程池+future,另一种futuretask的方法。线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。

// 创建线程池
 ExecutorService threadPool = Executors.newFixedThreadPool(10);
 while(true) {
 threadPool.execute(new Runnable() { // 提交多个线程任务,并执行
 @Override
 public void run() {
 System.out.println(Thread.currentThread().getName() + " is running ..");
 try {
 Thread.sleep(3000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 });
 } }

总结

理论上实现线程的方法还有一些,本文所提及到的,基本都是一些创建线程常用的方法。希望本文对大家在学习线程的过程中有所帮助。

相关文章

  • java基于spring注解AOP的异常处理的方法

    java基于spring注解AOP的异常处理的方法

    本篇文章主要介绍了java基于spring注解AOP的异常处理的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-02-02
  • 超个性修改SpringBoot项目的启动banner的方法

    超个性修改SpringBoot项目的启动banner的方法

    这篇文章主要介绍了超个性修改SpringBoot项目的启动banner的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • 初识Spring Boot框架之Spring Boot的自动配置

    初识Spring Boot框架之Spring Boot的自动配置

    本篇文章主要介绍了初识Spring Boot框架之Spring Boot的自动配置,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-04-04
  • java读取文件显示进度条的实现方法

    java读取文件显示进度条的实现方法

    当读取一个大文件时,一时半会儿无法看到读取结果,就需要显示一个进度条,是程序员明白已经读了多少文件,可以估算读取还需要多少时间,下面的代码可以实现这个功能
    2014-01-01
  • SpringMvc定制化深入探究原理

    SpringMvc定制化深入探究原理

    SpringMVC是一种基于Java,实现了Web MVC设计模式,请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将Web层进行职责解耦,这篇文章主要介绍了SpringMvc定制化原理
    2022-10-10
  • Java泛型类与泛型方法的定义详解

    Java泛型类与泛型方法的定义详解

    这篇文章主要介绍了Java泛型类与泛型方法的定义,结合实例形式详细分析了java泛型类与泛型方法定义、用法及相关操作注意事项,需要的朋友可以参考下
    2019-08-08
  • SpringBoot okhtt工具类封装方式

    SpringBoot okhtt工具类封装方式

    本文介绍了如何在SpringBoot项目中使用OkHttp工具类进行HTTP请求,并提供了一个封装了GET和POST方法的工具类示例,在Controller中使用该工具类时,需要注意OkHttpClient的静态初始化和异常处理
    2025-03-03
  • java文件读写操作实例详解

    java文件读写操作实例详解

    java的io流读取数据使用io流读取文件和向文件中写数据,这篇文章主要给大家介绍了关于java文件读写操作的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • Java中synchronized关键字的使用和原理详解

    Java中synchronized关键字的使用和原理详解

    这篇文章主要介绍了Java中synchronized关键字的使用和原理详解,JVM 是通过进入、退出 对象监视器(Monitor)来实现对方法、同步块的同步的,而对象监视器的本质依赖于底层操作系统的互斥锁实现,需要的朋友可以参考下
    2023-09-09
  • JavaWeb Refresh响应头代码实例详解

    JavaWeb Refresh响应头代码实例详解

    这篇文章主要介绍了JavaWeb Refresh响应头代码实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02

最新评论