美文网首页
【Java并发学习】之线程的创建

【Java并发学习】之线程的创建

作者: 颜洛滨 | 来源:发表于2017-10-23 12:41 被阅读18次

    【Java并发学习】之线程的创建

    前言

    Java并发一直是学习中的一个难点,之前虽然也有接触一部分的内容,不过由于当时对于并发的认识不足,所以学习的效果不是很好,乘着最近有时间,重新拾起这一部分内容,重新学习一下,并且将学习过程的笔记整理出来,本小节主要学习的内容是线程的创建

    线程的相关概念

    谈到并发,不可避免的会涉及到进程、线程相关的概念问题,关于这部分,有比较详细的定义,这里只是做个简单的总结与梳理

    进程

    进程简单的可以理解为是程序的一次执行,是动态的一个概念,进程是现在计算机体系结构中资源分配的最小单位,这里需要注意的是,在没有使用线程之前,进程还是系统进行调度的最小单位,不过由于对进程进行调度时所需要的时间以及空间代价比较大,所以现在进程一般只作为资源分配的最小单位,而不是调度的单位

    线程

    上面提到了,线程现在普通作为调度的最小单位,一个进程中至少包含一个线程,并且可以创建多个线程,这些线程之间共享进程所拥有的资源,并且可以执行不同的任务

    Java中多线程的创建

    上面我们简单了解了进程与线程的概念之后,接下来我们来学习Java中线程的创建与使用

    在具体使用之前,我们需要明确两个概念

    任务,任务是指具线程所要执行的具体内容,也就是所要执行的代码,需要明确的是,任务跟线程是没有太明确的关系的,任务是描述,而线程则是具体执行的工具

    线程,线程只是负责执行委托给它的任务,不清楚具体的内容是什么,因为具体的内容是定义在任务之中

    明确了这两个概念之后,接着来看下Java中所提供的实现多线程的工具

    任务的描述

    任务描述的方式总体上来看有两种,一种是没有具体返回值的任务,另一个一种是可以有返回值的任务,分别对应的接口是RunnableCallable<T>

    接下来我们来看下具体描述任务的方式

    
    /**
     * 计算任务描述
     * 实现Runnable接口并且重写run方法
     */
    class CalcTask1 implements Runnable{
    
        /**
         * 具体的任务描述
         * 稍后具体的任务会有对应的线程来执行
         */
        @Override
        public void run() {
            int sum = 0;
            for (int i = 0; i < 100000; i++){
                sum +=i ;
            }
            System.out.println(sum);
        }
    }
    
    
    
    /**
     * 计算任务描述
     * 实现Callable接口并重写call方法
     */
    class CalcTask2 implements Callable<Integer>{
    
        /**
         * 具体的任务描述
         * 稍后具体的任务会有对应的线程来执行
         */
        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for (int i = 0; i < 100000; i++){
                sum +=i ;
            }
            return sum;
        }
    }
    
    

    需要注意的是,Callable接口是泛型设计,需要为其指定对应的返回类型

    除了上面的这两种描述方式外,其实还有另外一种,那就是直接继承Thread类,并且重写对应的run方法,这种方式也是可以用于描述对应的任务,不过会导致具体任务与对应的线程绑定在一起

    
    /**
     * 通过继承Thread并且重写run方式来描述任务
     */
    class CalcTask3 extends Thread{
    
        @Override
        public void run() {
            int sum = 0;
            for (int i = 0; i < 100000; i++){
                sum +=i ;
            }
            System.out.println(sum);
        }
    }
    
    

    使用多线程的方式

    上面我们提到了,在Java中,在使用多线程的时候,一般是把操作逻辑与具体的执行者分开,这种实现方式看上去比较麻烦,但是实际上将实现逻辑与具体执行者分离是一种非常好的设计,由于线程的创建以及回收时非常耗费资源的,所以为了避免频繁的创建与回收线程,我们可以采用线程池的方式来管理线程,而如果是将实现逻辑与具体的执行者绑定,就无法充分利用这种优势了

    从本质上来讲,实现多线程的方式只有一种,那就是创建对应的线程,并且将具体的操作逻辑交给线程,并且调用对应线程的start方法,如下所示

    
    // 创建对应的任务
    Runnable calcTask = new CalcTask1();
    // 将任务提交给线程
    // 这里需要注意的是,如果是直接继承自Thread,则直接调用对应的
    // 线程对象的run方法即可
    Thread executor = new Thread(calcTask);
    // 启动线程
    executor.start();
    
    

    上面这种实现方式是比较原始的方式,通过这种方式来启动多线程的缺点是,在需要使用线程的时候,必须自己手动创建,也就是说,无法充分利用线程池的优势,而且,还必须自己管理线程的生命周期,于是,在JDK5的时候,Java引入了一个通用的线程执行框架Executor(关于Executor将在后面学习到),从而简化了这一系列的操作过程

    
    // 获取一个ExecutorService实例
    ExecutorService service = Executors.newCachedThreadPool();
    // 创建对应的任务
    Runnable calcTask = new CalcTask1();
    // 将任务提交给ExecutorService
    service.submit(calcTask);
    // 关闭对应的ExecutorService
    service.shutdown();
    
    

    总结

    本小节主要学习了进程、线程的相关概念,以及在Java中描述任务的方式,实现 RunnableCallable<T>以及继承Thread,并且学习学习了启动线程来驱动对应任务的方式,直接将任务交给Thread对象,或者将其交给Executor框架

    相关文章

      网友评论

          本文标题:【Java并发学习】之线程的创建

          本文链接:https://www.haomeiwen.com/subject/hizouxtx.html