1、进程与线程的概念

现在几乎所有操作系统都支持多任务,通常一个任务就是一个程序,一个运行中的程序就是一个进程。当一个程序行时,其内部也可能在执行多个任务,进程内每一个任务的执行流,就是一个线程。

所以线程也被称作轻量级进程。

总而言之,线程是进程的组成部分,可以独立、并发的执行任务

2、线程的创建和启动

Java中有两种方式来创建和启动线程。

2.1继承Thread类创建和启动线程

通过继承Thread类创建并启动多线程的步骤如下:

1、创建Thread的子类,并重写run方法。run方法中就是线程要执行的任务,所以也把run方法称为线程执行体。

2、创建该子类的实例,即线程对象。

3、使用线程对象的start方法启动线程。

线程类代码如下:

 //通过继承Thread类来创建线程类

 public class ThreadOne extends Thread{

     private int i;

     //重写run方法,方法体就是线程要执行的任务

     @Override

     public void run() {

        for (i = 0; i < 20; i++) {

            //继承Thread类时,可以直接调用getName()方法来返回当前线程的名字

            //如果想获取当前线程,直接使用this即可

            System.out.println(getName()+" "+i);

        }

     }

 }

测试代码如下:

 public class TestThreadOne {

     public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {

            //打印主线线程的信息

            System.out.println(Thread.currentThread().getName()+" "+i);

            if (i==3) {

               //创建并启动第一条线程

               new ThreadOne().start();

               //创建并启动第二条线程

               new ThreadOne().start();

            }

        }

     }

 }

部分结果如下:

main 0
main 1
main 2
main 3
Thread-0 0
Thread-1 0
main 4
Thread-1 1
Thread-0 1
Thread-1 2
main 5

从上面的运行结果可以发现,测试类只显式的创建了两条线程,但实际上有三条线程在运行,即主线程、线程0和线程1。其中主线程的线程体是main方法里的内容。

此外,Thread-0和Thread-1两条线程输出的变量i的值是不连续的,而i并非是局部变量,而是实例成员变量,从这一点可以看出,每次创建线程都创建了一个ThreadOne对象。

2.2实现Runnable接口创建线程

实现Runnable接口创建并启动线程的步骤如下:

1、创建Runnable接口的实现类,重写run方法。run方法的内容即是线程要执行的任务。

2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象。该Thread对象才是真正的线程类。

线程类代码如下:

 //通过实现Runnable接口创建线程类

 public class ThreadTwo implements Runnable{

     private int i;

     //run方法同样是线程的执行体

     @Override

     public void run() {

        for (i = 0; i < 5; i++) {

            //实现Runnable接口创建线程类时,只能使用Thread.currentThread()来获取当前线程

            System.out.println(Thread.currentThread().getName()+" "+i);

        }

     }

 }

测试代码如下:

 public class TestThreadTwo {

     public static void main(String[] args) {

        for (int i = 0; i < 5; i++) {

            //打印主线线程的信息

            System.out.println(Thread.currentThread().getName()+" "+i);

            if (i==3) {

               ThreadTwo threadTwo = new ThreadTwo();

               //创建并启动第一条线程

               new Thread(threadTwo).start();

               //创建并启动第二条线程

               new Thread(threadTwo).start();

            }

        }

     }

 }

运行结果如下:

 main 0

 main 1

 main 2

 main 3

 Thread-0 0

 Thread-1 0

 main 4

 Thread-1 2

 Thread-0 1

 Thread-0 4

 Thread-1 3

从运行结果可以看出,Thread-0和Thread-1打印的i值是连续的,说明这两条线程是共享一个实例的。这是因为,我们所创建的两个Thread类使用同一个Runnable对象作为target。

在实际开发中,推荐使用实现Runnable接口的方式来创建线程,这样还可以实现或继

2.3有返回值的线程

从JDK1.5开始,java提供Callable接口和Future接口以获得线程的返回值。

Callable类似Runnable的加强版,提供一个call()方法作为线程的执行体。与Runnable的run()方法相比,call()方法更强大:

  • call()方法可以有返回值
  • call()可以声明抛出异常

不过Callable对象并不能直接作为Thread的target,必须使用Future对象包装一下。具体使用步骤如下:

  • 创建Callable的实现类,并实现call方法,注意有泛型限制。
  • 使用FutureTask包装Callable对象。
  • 把FutureTask作为target传入Thread,创建启动线程。
  • 调用FutureTask对象的方法获取返回值。

线程代码:

 public class CallableOne implements Callable<Integer>{

     @Override

     public Integer call() throws Exception {

        int i = 0;

        for(i=0;i<10;i++){

            System.out.println(Thread.currentThread().getName()+" "+i);

        }

        //线程返回值,模拟返回前阻塞主线程

        Thread.sleep(5000);

        return i;

     }

 }

测试代码:

 public class TestCallable {

     public static void main(String[] args) {

        //创建Callable对象

        CallableOne callableOne = new CallableOne();

        //使用FutureTask包装Callable对象

        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableOne);

        for(int i=0;i<100;i++){

            System.out.println(Thread.currentThread().getName()+" "+i);

            //i等于3时启动子线程,此时可见主线程与子线程交替执行

            if (i==3) {

               //FutureTask是Runnable的子类,可传入Thread当作target

               new Thread(futureTask).start();

            }

            //i等于80的时候来获取子线程的返回值,此时主线程会阻塞,直到返回了结果

            if (i==80) {

               try {

                   System.out.println("线程的返回值:"+futureTask.get());

               } catch (Exception e) {

                   e.printStackTrace();

               }

            }

        }

     }

 }

以上代码中,在i等于3时启动子线程,此时可见主线程与子线程交替执行。在i等于80的时候来获取子线程的返回值,此时主线程会阻塞,直到返回结果。

java多线程回顾1:线程的概念与创建的更多相关文章

  1. 廖雪峰Java11多线程编程-1线程的概念-2创建新线程

    Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...

  2. java多线程总结:线程的两种创建方式及优劣比较

    1.通过实现Runnable接口线程创建 (1).定义一个类实现Runnable接口,重写接口中的run()方法.在run()方法中加入具体的任务代码或处理逻辑. (2).创建Runnable接口实现 ...

  3. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  4. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  5. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  6. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  7. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  8. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  9. Java多线程——进程和线程

    Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...

  10. Java多线程之守护线程

    Java多线程之守护线程 一.前言 Java线程有两类: 用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程等都是用户线程 守护线程:运行在后台,为其他前台线程服务 特点:一旦所有用 ...

随机推荐

  1. LeetCode初级算法--字符串01:反转字符串

    LeetCode初级算法--字符串01:反转字符串 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...

  2. django中app分组

    08.13自我总结 django中app分组 一.django路由系统app进行分组 1.创建app 使用pycharm创建django的时候, 加上app的名字,后续多个app只需复制粘贴之前app ...

  3. Cocos2d-x 学习笔记(11.6) Sequence

    1. Sequence 动作序列.动作按参数顺序执行,动作总时长为每个动作的时长之和. 1.1 成员变量 FiniteTimeAction *_actions[]; float _split; // ...

  4. 《Java并发编程实战》读书笔记-第3章 对象的共享

    可见性 在没有同步的情况下,编译器.处理器以及运行时都可能做指令重排.执行结果可能会出现错误 volatile变量 编译器与运行时不会进行指令重排,不会进行缓存,使用volatile变量要满足以下条件 ...

  5. 案例_(单线程)使用xpath爬取糗事百科

    案例_(单线程)使用xpath爬取糗事百科 步骤如下: 首先通过xpath插件找出我们要爬取的信息的匹配规则 url = "https://www.qiushibaike.com/8hr/p ...

  6. selenium-模块概述(1)

    Selenium是一个用于Web应用程序自动化测试工具.Selenium测试直接运行在浏览器中,就像真正的用户在操作一样. 1.目录结构如下: D:\soft\python36\Lib\site-pa ...

  7. Linux面试题-7

    在日常管理中,通常CPU会影响系统性能的情况是: A A:CPU已满负荷地运转 B:CPU的运行效率为30% C:CPU的运行效率为50% D:CPU的运行效率为80% 下面那个命令可以终止一个用户的 ...

  8. 使用SQLserver Management Studio连接VS2012自带数据库

    下载 Microsoft® SQL Server® 2008 Management Studio Express http://www.microsoft.com/zh-CN/download/det ...

  9. 设计模式(二十)Flyweight模式

    当使用new关键字生成类的实例时,需要给其分配足够的内存空间.当程序中需要大量对象时,如果都是用new关键字来分配内存,将会消耗大量内存空间.Flyweight模式就是尽量避免new出实例,而是通过尽 ...

  10. 在k8s上安装Jenkins及常见问题

    持续集成和部署是DevOps的重要组成部分,Jenkins是一款非常流行的持续集成和部署工具,最近试验了一下Jenkins,发现它是我一段时间以来用过的工具中最复杂的.一个可能的原因是它需要与各种其它 ...