Java开发入门到精通--java线程实现/创建的几种方式
小职 2021-08-25 来源 : 阅读 271 评论 0

摘要:本文主要介绍了Java开发入门到精通--java线程实现/创建的几种方式,通过具体的内容向大家展现,希望对大家Java开发的学习有所帮助。

本文主要介绍了Java开发入门到精通--java线程实现/创建的几种方式,通过具体的内容向大家展现,希望对大家Java开发的学习有所帮助。

Java开发入门到精通--java线程实现/创建的几种方式

java线程的创建与实现

进程与线程

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


第一种方式继承Thread类

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

Java开发入门到精通--java线程实现/创建的几种方式


继承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方法便结束线程。

Java开发入门到精通--java线程实现/创建的几种方式


第二种创建线程的方法,实现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();

    }


}


结果

Java开发入门到精通--java线程实现/创建的几种方式


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

Callable接口使用方法和Runnable接口的方法类似不同的一点是Callable接口具有返回值,返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。 Executor线程池的超类:执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。例如,可能会使用以下方法,而不是为一组任务中的每个任务调用 new Thread(new(RunnableTask())).start():


实现代码

对下列代码进行分析

首先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();


    }

}



执行结果

Java开发入门到精通--java线程实现/创建的几种方式


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

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


     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())+"毫秒");

     }


Callable接口形式上和Runnable接口类似,区别在于Callable接口可以处理异常,以及可以拥有返回值。比Runnable更灵活,弊端在于Callable不能用Thread线程类进行封装,需要借助FutureTask类,FutuTask也实现了Runnable接口,初始化可以将Callable对象作为参数传入到FutureTask里,然后在采用Thread封装FutureTask。最后调用start()启动线程,此时线程会去执行Callable的call()方法里面的类容。


总结

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


我是小职,记得找我

✅ 解锁高薪工作

✅ 免费获取基础课程·答疑解惑·职业测评

Java开发入门到精通--java线程实现/创建的几种方式

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程