• java多线程回顾1:线程的概念与创建


    1、进程与线程的概念 

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

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

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

    2、线程的创建和启动

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

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

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

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

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

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

    线程类代码如下:

     1 //通过继承Thread类来创建线程类
     2 
     3 public class ThreadOne extends Thread{
     4 
     5     private int i;
     6 
     7    
     8 
     9     //重写run方法,方法体就是线程要执行的任务
    10 
    11     @Override
    12 
    13     public void run() {
    14 
    15        for (i = 0; i < 20; i++) {
    16 
    17            //继承Thread类时,可以直接调用getName()方法来返回当前线程的名字
    18 
    19            //如果想获取当前线程,直接使用this即可
    20 
    21            System.out.println(getName()+" "+i);
    22 
    23        }
    24 
    25     }
    26 
    27 }

    测试代码如下:

     1 public class TestThreadOne {
     2 
     3     public static void main(String[] args) {
     4 
     5        for (int i = 0; i < 10; i++) {
     6 
     7            //打印主线线程的信息
     8 
     9            System.out.println(Thread.currentThread().getName()+" "+i);
    10 
    11            if (i==3) {
    12 
    13               //创建并启动第一条线程
    14 
    15               new ThreadOne().start();
    16 
    17               //创建并启动第二条线程
    18 
    19               new ThreadOne().start();
    20 
    21            }
    22 
    23        }
    24 
    25     }
    26 
    27 }

    部分结果如下:

    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对象才是真正的线程类。

    线程类代码如下:

     1 //通过实现Runnable接口创建线程类
     2 
     3 public class ThreadTwo implements Runnable{
     4 
     5     private int i;
     6 
     7    
     8 
     9     //run方法同样是线程的执行体
    10 
    11     @Override
    12 
    13     public void run() {
    14 
    15        for (i = 0; i < 5; i++) {
    16 
    17            //实现Runnable接口创建线程类时,只能使用Thread.currentThread()来获取当前线程
    18 
    19            System.out.println(Thread.currentThread().getName()+" "+i);
    20 
    21        }
    22 
    23     }
    24 
    25 }

    测试代码如下:

     1 public class TestThreadTwo {
     2 
     3     public static void main(String[] args) {
     4 
     5        for (int i = 0; i < 5; i++) {
     6 
     7            //打印主线线程的信息
     8 
     9            System.out.println(Thread.currentThread().getName()+" "+i);
    10 
    11            if (i==3) {
    12 
    13               ThreadTwo threadTwo = new ThreadTwo();
    14 
    15               //创建并启动第一条线程
    16 
    17               new Thread(threadTwo).start();
    18 
    19               //创建并启动第二条线程
    20 
    21               new Thread(threadTwo).start();
    22 
    23            }
    24 
    25        }
    26 
    27     }
    28 
    29 }

    运行结果如下:

     1 main 0
     2 
     3 main 1
     4 
     5 main 2
     6 
     7 main 3
     8 
     9 Thread-0 0
    10 
    11 Thread-1 0
    12 
    13 main 4
    14 
    15 Thread-1 2
    16 
    17 Thread-0 1
    18 
    19 Thread-0 4
    20 
    21 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对象的方法获取返回值。

    线程代码:

     1 public class CallableOne implements Callable<Integer>{
     2 
     3     @Override
     4 
     5     public Integer call() throws Exception {
     6 
     7        int i = 0;
     8 
     9        for(i=0;i<10;i++){
    10 
    11            System.out.println(Thread.currentThread().getName()+" "+i);
    12 
    13        }
    14 
    15        //线程返回值,模拟返回前阻塞主线程
    16 
    17        Thread.sleep(5000);
    18 
    19        return i;
    20 
    21     }
    22 
    23 }

    测试代码:

     1 public class TestCallable {
     2 
     3     public static void main(String[] args) {
     4 
     5        //创建Callable对象
     6 
     7        CallableOne callableOne = new CallableOne();
     8 
     9        //使用FutureTask包装Callable对象
    10 
    11        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableOne);
    12 
    13        for(int i=0;i<100;i++){
    14 
    15            System.out.println(Thread.currentThread().getName()+" "+i);
    16 
    17            //i等于3时启动子线程,此时可见主线程与子线程交替执行
    18 
    19            if (i==3) {
    20 
    21               //FutureTask是Runnable的子类,可传入Thread当作target
    22 
    23               new Thread(futureTask).start();
    24 
    25            }
    26 
    27            //i等于80的时候来获取子线程的返回值,此时主线程会阻塞,直到返回了结果
    28 
    29            if (i==80) {
    30 
    31               try {
    32 
    33                   System.out.println("线程的返回值:"+futureTask.get());
    34 
    35               } catch (Exception e) {
    36 
    37                   e.printStackTrace();
    38 
    39               }
    40 
    41            }
    42 
    43        }
    44 
    45     }
    46 
    47 }

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

  • 相关阅读:
    19-10-31-B
    19-10-30-Night-V
    19-10-30-C
    19-10-29-Night-X
    19-10-29-Z
    19-10-28-A
    19-10-27-S
    19-10-26-Night-D
    留言板
    优秀博客存档
  • 原文地址:https://www.cnblogs.com/bailiyi/p/5309566.html
Copyright © 2020-2023  润新知